import crypto from "crypto";
import { execFileSync } from "child_process";
import os from "os";
import type { LicenseConfig, LicenseHeartbeatStatus, LicenseStatus, RiskLimits } from "@/lib/types";

export type RemoteHeartbeatResult =
  | { ok: true; license: Partial<LicenseConfig>; limits?: Partial<RiskLimits>; raw: Record<string, unknown> }
  | { ok: false; license: Partial<LicenseConfig>; error: string; code: string; status?: number };

const STATUSES: LicenseStatus[] = ["trial", "active", "expired", "suspended"];
const PLANS: LicenseConfig["plan"][] = ["starter", "growth", "pro", "enterprise"];
const DEFAULT_REMOTE_GRACE_HOURS = 5;
const PRODUCT_SLUG = "pinterest-auto";

export function isRemoteLicenseRequired() {
  return truthyEnv(process.env.PINTEREST_AUTO_REQUIRE_REMOTE_LICENSE);
}

export function getMachineId() {
  const platformId = getPlatformMachineId();
  const identity = [
    "pinterest-auto-license-v1",
    os.platform(),
    os.arch(),
    platformId,
    os.hostname(),
    safeUserField("username"),
    safeUserField("homedir"),
  ].join("|");
  return crypto.createHash("sha256").update(identity).digest("hex");
}

export function isLicenseOperational(license: LicenseConfig) {
  const now = Date.now();
  const expiresAt = new Date(license.expiresAt).getTime();
  if (license.status !== "active" && license.status !== "trial") {
    return { ok: false as const, code: "license_inactive", error: `当前授权状态为 ${license.status}，请续费或启用授权后再执行。` };
  }
  if (Number.isFinite(expiresAt) && expiresAt < now) {
    return { ok: false as const, code: "license_expired", error: "当前授权已过期，请续费后再执行。" };
  }
  const remote = evaluateRemoteHeartbeat(license);
  if (!remote.ok) {
    return { ok: false as const, code: remote.code, error: remote.error };
  }
  return { ok: true as const };
}

export function evaluateRemoteHeartbeat(license: LicenseConfig, now = new Date()) {
  if (!license.remoteServerUrl && !license.remoteLicenseKey) {
    if (isRemoteLicenseRequired()) {
      return {
        ok: false as const,
        status: "failed" as LicenseHeartbeatStatus,
        code: "remote_license_required",
        error: "此客户端已启用云端远程授权，请先填写 Web 云端授权地址和 License Key。",
      };
    }
    return { ok: true as const, status: "not_configured" as LicenseHeartbeatStatus };
  }
  if (!license.remoteServerUrl || !license.remoteLicenseKey) {
    return { ok: false as const, status: "failed" as LicenseHeartbeatStatus, code: "remote_license_incomplete", error: "远程心跳未完整配置，请填写 License Server URL 和 License Key。" };
  }
  if (license.remoteStatus === "suspended" || license.remoteStatus === "expired") {
    return { ok: false as const, status: "expired" as LicenseHeartbeatStatus, code: "remote_license_inactive", error: `远程授权状态为 ${license.remoteStatus}，商业动作已停用。` };
  }

  const nowMs = now.getTime();
  const leaseMs = new Date(license.leaseExpiresAt || "").getTime();
  if (Number.isFinite(leaseMs) && leaseMs >= nowMs) {
    return { ok: true as const, status: "ok" as LicenseHeartbeatStatus };
  }

  const lastHeartbeatMs = new Date(license.lastHeartbeatAt || "").getTime();
  const graceHours = clampInt(license.graceHours, DEFAULT_REMOTE_GRACE_HOURS, 1, 72);
  if (Number.isFinite(lastHeartbeatMs) && nowMs - lastHeartbeatMs <= graceHours * 3600000) {
    return { ok: true as const, status: "grace" as LicenseHeartbeatStatus };
  }

  return {
    ok: false as const,
    status: "expired" as LicenseHeartbeatStatus,
    code: "remote_heartbeat_expired",
    error: `远程心跳租约已过期，且超过 ${graceHours} 小时宽限期。请联网刷新授权。`,
  };
}

export async function refreshRemoteHeartbeat(license: LicenseConfig): Promise<RemoteHeartbeatResult> {
  const remoteServerUrl = normalizeRemoteBaseUrl(license.remoteServerUrl || process.env.PINTEREST_AUTO_LICENSE_SERVER_URL || "");
  const remoteLicenseKey = String(license.remoteLicenseKey || process.env.PINTEREST_AUTO_LICENSE_KEY || "").trim();
  const now = new Date().toISOString();
  if (!remoteServerUrl || !remoteLicenseKey) {
    const remoteRequired = isRemoteLicenseRequired();
    return {
      ok: false,
      code: remoteRequired ? "remote_license_required" : "remote_license_incomplete",
      error: remoteRequired
        ? "此客户端已启用云端远程授权，请先填写 Web 云端授权地址和 License Key。"
        : "远程心跳未配置 License Server URL 或 License Key。",
      license: {
        heartbeatStatus: remoteRequired ? "failed" : "not_configured",
        heartbeatError: remoteRequired ? "remote_license_required" : "remote_license_incomplete",
      },
    };
  }

  try {
    const response = await fetch(new URL("/api/licenses/heartbeat", remoteServerUrl), {
      method: "POST",
      headers: {
        authorization: `Bearer ${remoteLicenseKey}`,
        "content-type": "application/json",
      },
      body: JSON.stringify({
        productSlug: PRODUCT_SLUG,
        machineId: getMachineId(),
        appVersion: process.env.npm_package_version || "local",
        clientTime: now,
      }),
      cache: "no-store",
      signal: AbortSignal.timeout(clampInt(process.env.PINTEREST_AUTO_LICENSE_HEARTBEAT_TIMEOUT_MS, 10000, 1000, 60000)),
    });
    const data = await response.json().catch(() => ({})) as Record<string, unknown>;
    if (!response.ok || data.ok === false) {
      return {
        ok: false,
        status: response.status,
        code: String(data.error || `http_${response.status}`),
        error: String(data.error || response.statusText || "远程心跳失败。"),
        license: {
          remoteServerUrl,
          remoteLicenseKey,
          heartbeatStatus: "failed",
          heartbeatError: String(data.error || response.statusText || "remote_heartbeat_failed"),
        },
      };
    }

    const remoteStatus = normalizeStatus(data.status);
    const plan = normalizePlan(data.plan);
    const licensePatch: Partial<LicenseConfig> = {
      licenseId: stringValue(data.licenseId) || license.licenseId,
      machineId: stringValue(data.machineId) || getMachineId(),
      plan: plan || license.plan,
      status: remoteStatus || license.status,
      remoteStatus: remoteStatus || license.remoteStatus,
      expiresAt: isoDate(data.expiresAt) || license.expiresAt,
      remoteServerUrl,
      remoteLicenseKey,
      lastHeartbeatAt: isoDate(data.serverTime) || now,
      nextHeartbeatAt: isoDate(data.nextHeartbeatAt) || undefined,
      leaseExpiresAt: isoDate(data.leaseExpiresAt) || undefined,
      graceHours: clampInt(data.graceHours, DEFAULT_REMOTE_GRACE_HOURS, 1, 72),
      heartbeatStatus: remoteStatus === "expired" || remoteStatus === "suspended" ? "expired" : "ok",
      heartbeatError: "",
      verifiedAt: now,
      verificationStatus: "valid",
      remoteUsageSeconds: usageSeconds(data.usage),
    };
    return {
      ok: true,
      license: licensePatch,
      limits: normalizeRemoteLimits(data.limits),
      raw: data,
    };
  } catch (error) {
    return {
      ok: false,
      code: "remote_heartbeat_request_failed",
      error: error instanceof Error ? error.message : String(error),
      license: {
        remoteServerUrl,
        remoteLicenseKey,
        heartbeatStatus: "failed",
        heartbeatError: error instanceof Error ? error.message : String(error),
      },
    };
  }
}

function getPlatformMachineId() {
  try {
    if (process.platform === "darwin") {
      return execFileSync("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"], { encoding: "utf8", timeout: 3000 })
        .match(/"IOPlatformUUID"\s=\s"([^"]+)"/)?.[1] || os.hostname();
    }
    if (process.platform === "win32") {
      return execFileSync("wmic", ["csproduct", "get", "UUID"], { encoding: "utf8", timeout: 3000 })
        .split(/\r?\n/)
        .map((line) => line.trim())
        .find((line) => line && line.toLowerCase() !== "uuid") || os.hostname();
    }
    return execFileSync("cat", ["/etc/machine-id"], { encoding: "utf8", timeout: 3000 }).trim() || os.hostname();
  } catch {
    return os.hostname();
  }
}

function safeUserField(field: "username" | "homedir") {
  try {
    const info = os.userInfo();
    return field === "username" ? info.username : info.homedir;
  } catch {
    return "";
  }
}

function normalizeRemoteBaseUrl(value: string) {
  const raw = String(value || "").trim().replace(/\/+$/, "");
  if (!raw) return "";
  return /^https?:\/\//i.test(raw) ? raw : `https://${raw}`;
}

function normalizeStatus(value: unknown): LicenseStatus | undefined {
  const text = String(value || "").trim().toLowerCase();
  return STATUSES.includes(text as LicenseStatus) ? text as LicenseStatus : undefined;
}

function normalizePlan(value: unknown): LicenseConfig["plan"] | undefined {
  const text = String(value || "").trim().toLowerCase();
  return PLANS.includes(text as LicenseConfig["plan"]) ? text as LicenseConfig["plan"] : undefined;
}

function normalizeRemoteLimits(value: unknown): Partial<RiskLimits> | undefined {
  if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
  const raw = value as Record<string, unknown>;
  return removeUndefined({
    dailyTrendResearches: positiveNumber(raw.dailyTrendResearches),
    dailyPinPublishes: positiveNumber(raw.dailyPinPublishes),
    dailyBoardCreates: positiveNumber(raw.dailyBoardCreates),
    dailyApiWrites: positiveNumber(raw.dailyApiWrites),
    requireApprovalForPublish: booleanValue(raw.requireApprovalForPublish),
    requireApprovalForFollow: booleanValue(raw.requireApprovalForFollow),
    requireApprovalForComment: booleanValue(raw.requireApprovalForComment),
    requireApprovalForMessage: booleanValue(raw.requireApprovalForMessage),
  });
}

function positiveNumber(value: unknown) {
  const parsed = Number(value);
  return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : undefined;
}

function booleanValue(value: unknown) {
  return typeof value === "boolean" ? value : undefined;
}

function usageSeconds(value: unknown) {
  if (!value || typeof value !== "object" || Array.isArray(value)) return 0;
  const total = Number((value as Record<string, unknown>).totalUsageSeconds);
  return Number.isFinite(total) ? Math.max(0, Math.floor(total)) : 0;
}

function isoDate(value: unknown) {
  const text = stringValue(value);
  if (!text) return "";
  const date = new Date(text);
  return Number.isNaN(date.getTime()) ? "" : date.toISOString();
}

function stringValue(value: unknown) {
  return typeof value === "string" ? value.trim() : "";
}

function clampInt(value: unknown, fallback: number, min: number, max: number) {
  const parsed = typeof value === "number" ? value : parseInt(String(value || ""), 10);
  if (!Number.isFinite(parsed)) return fallback;
  return Math.max(min, Math.min(max, Math.floor(parsed)));
}

function truthyEnv(value: unknown) {
  return /^(1|true|yes|on)$/i.test(String(value || "").trim());
}

function removeUndefined<T extends Record<string, unknown>>(value: T): Partial<T> {
  return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined)) as Partial<T>;
}
