import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
import { dirname } from "node:path";
import type { PurchasePlan, PurchasePlanResponse } from "../shared/contract.js";
import type { PurchasePlanStorage } from "../src/core/types.js";

interface StoredPlan {
  updatedAt: number;
  plan: PurchasePlan;
}

export class JsonPlanStorage implements PurchasePlanStorage {
  constructor(private readonly filePath: string) {}

  async get(): Promise<StoredPlan | null> {
    try {
      const value = JSON.parse(await readFile(this.filePath, "utf8")) as unknown;
      if (!isStoredPlan(value)) throw new Error("invalid purchase-plan file");
      return value;
    } catch (error) {
      if (isMissingFile(error)) return null;
      throw error;
    }
  }

  async set(plan: PurchasePlan, updatedAt = Date.now()): Promise<number> {
    const tmp = `${this.filePath}.tmp`;
    await mkdir(dirname(this.filePath), { recursive: true });
    await writeFile(tmp, `${JSON.stringify({ plan, updatedAt }, null, 2)}\n`, { mode: 0o600 });
    await rename(tmp, this.filePath);
    return updatedAt;
  }

  async loadResponse(): Promise<PurchasePlanResponse> {
    const stored = await this.get();
    return stored ?? { plan: null, updatedAt: null };
  }
}

function isStoredPlan(value: unknown): value is StoredPlan {
  if (!value || typeof value !== "object") return false;
  const candidate = value as Partial<StoredPlan>;
  return Number.isFinite(candidate.updatedAt) && !!candidate.plan && typeof candidate.plan === "object";
}

function isMissingFile(error: unknown): boolean {
  return !!error && typeof error === "object" && "code" in error && error.code === "ENOENT";
}
