import fs from "fs";
import path from "path";
import { redeem, activate } from "./license";

/**
 * First-run setup state.
 *
 * Kept in install.json at the project root (not the database) so the
 * "is this installed yet?" check never depends on the database being
 * readable. Grandfathering: an existing site that already has a
 * data/db.json but no install.json is treated as already installed, so
 * updating an existing copy never drops it back into the wizard.
 */

const INSTALL_FILE = path.join(process.cwd(), "install.json");

export const PRODUCT_SLUG = "socialscript";

/**
 * The version this copy is running.
 *
 * Read from package.json rather than written here, because an update
 * ships a new package.json — so the number the site reports and the
 * number the update was built from can never drift apart. A missing or
 * unreadable file falls back rather than crashing the app.
 */
export const PRODUCT_VERSION: string = (() => {
  try {
    const pkg = JSON.parse(
      fs.readFileSync(path.join(process.cwd(), "package.json"), "utf8")
    ) as { version?: string };
    return String(pkg.version ?? "1.0.0");
  } catch {
    return "1.0.0";
  }
})();
const LICENSE_BASE =
  process.env.LICENSE_SERVER ?? "https://license.hifod.com/api";

export type InstallState = {
  installed: boolean;
  /**
   * Set the moment an update finishes writing files, cleared once the app
   * restarts on that version. Next compiles ahead of time, so the new code
   * sits on disk doing nothing until a build and a restart — without this
   * marker the panel would keep offering the same update forever, because
   * the running process still reports the old version.
   */
  pendingVersion?: string;
  pendingSince?: string;
  siteName?: string;
  mailFrom?: string;
  licenseType?: string;
  license?: string;
  token?: string;
  installedAt?: string;
};

export function readInstall(): InstallState | null {
  try {
    return JSON.parse(fs.readFileSync(INSTALL_FILE, "utf8")) as InstallState;
  } catch {
    return null;
  }
}

export function writeInstall(state: InstallState) {
  fs.writeFileSync(INSTALL_FILE, JSON.stringify(state, null, 2));
}

/** Has the site been set up? */
export function isInstalled(): boolean {
  // Installed only once install.json says so. Existing pre-wizard sites are
  // migrated to installed by getDb() when it loads an existing database, so
  // they never land here as "not installed".
  return readInstall()?.installed === true;
}

type VerifyResult =
  | { ok: true; license: string; token?: string }
  | { ok: false; error: string };

/**
 * Verifies a purchase and, where possible, activates it for this domain.
 *
 * - codecanyon: the Envato purchase code is checked against the Envato
 *   sales API through our license server's Envato endpoint.
 * - scriptbull / portsale: the shop order number is redeemed through the
 *   normal license endpoint (reuses lib/license.ts).
 */
export async function verifyLicense(
  type: string,
  key: string,
  domain: string
): Promise<VerifyResult> {
  if (type === "codecanyon") {
    try {
      const res = await fetch(`${LICENSE_BASE}/request-license-envato.php`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          domain,
          purchase_code: key,
          product: PRODUCT_SLUG,
          version: PRODUCT_VERSION,
        }),
      });
      const d = await res.json().catch(() => null);
      if (!res.ok || !d?.success || !(d.license_key || d.license)) {
        return {
          ok: false,
          error: String(
            d?.error ?? d?.message ?? "We could not verify that purchase code."
          ),
        };
      }
      const license = String(d.license_key ?? d.license);
      const act = await activate(license, PRODUCT_SLUG, PRODUCT_VERSION);
      return { ok: true, license, token: act.ok ? act.token : undefined };
    } catch {
      return {
        ok: false,
        error: "Couldn't reach the license server. Try again in a moment.",
      };
    }
  }

  // scriptbull / portsale / a direct HI24 key
  const red = await redeem(key, PRODUCT_SLUG, PRODUCT_VERSION);
  if (!red.ok) return { ok: false, error: red.error };
  const act = await activate(red.license, PRODUCT_SLUG, PRODUCT_VERSION);
  return { ok: true, license: red.license, token: act.ok ? act.token : undefined };
}

/** Records the domain the app is being installed on, so license.ts can read it. */
export async function rememberDomain(host: string) {
  try {
    const { openStore } = await import("@/lib/data/store");
    const store = await openStore();
    const s = await store.settings();
    if (!s.siteUrl) {
      s.siteUrl = `https://${host}`;
      await store.putSmall("siteSettings", s);
      await store.save();
    }
  } catch {
    /* best effort */
  }
}
