import { createHmac, randomUUID } from "node:crypto";
import { CredentialStatus, SiteStatus, SubscriptionStatus } from "@prisma/client";
import { db } from "@/lib/db";
import { getEnv } from "@/lib/env";
import { decryptCredential } from "./credential-crypto";
import { assertPublicDnsTarget } from "./client";
import { normalizePublicSiteUrl } from "./url";

export async function syncSiteLicense(siteId: string, enabledByPlan = true) {
  const site = await db.site.findUnique({
    where: { id: siteId },
    include: {
      organization: { include: { subscription: { include: { plan: true } } } },
      credentials: { where: { status: CredentialStatus.ACTIVE }, orderBy: { createdAt: "desc" }, take: 1 },
    },
  });
  if (!site?.pluginSiteId || !site.credentials[0]) throw new Error("A paired WordPress credential is required to issue a license.");
  const subscription = site.organization.subscription;
  const active = Boolean(enabledByPlan && subscription && (subscription.status === SubscriptionStatus.ACTIVE || subscription.status === SubscriptionStatus.TRIALING));
  const plan = subscription?.plan;
  const token = decryptCredential(site.credentials[0].encryptedSecret, getEnv().CREDENTIAL_ENCRYPTION_KEY);
  const now = new Date();
  const validUntil = new Date(now.getTime() + 48 * 60 * 60 * 1000);
  const payload = {
    site_id: site.pluginSiteId,
    issued_at: now.toISOString(),
    valid_until: validUntil.toISOString(),
    active,
    plan_code: plan?.code ?? "",
    plan_name: plan?.name ?? "No active plan",
    features: {
      ai_chat: active && Boolean(plan?.allowAiChat),
      chat_analytics: active && Boolean(plan?.allowChatAnalytics),
      multi_site_jobs: active && Boolean(plan?.allowMultiSiteJobs),
      white_label: active && Boolean(plan?.allowWhiteLabel),
      client_seats: active && Boolean(plan?.clientSeatLimit),
    },
    limits: { sites: plan?.siteLimit ?? 0, client_seats: plan?.clientSeatLimit ?? 0 },
    trial_ends_at: subscription?.trialEndsAt?.toISOString() ?? "",
    current_period_end: subscription?.currentPeriodEnd?.toISOString() ?? "",
  };
  const body = JSON.stringify(payload);
  const url = normalizePublicSiteUrl(site.normalizedUrl);
  await assertPublicDnsTarget(url.hostname);
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), 12_000);
  try {
    const response = await fetch(new URL("/wp-json/site-agent/v1/license", url.origin), {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-Site-Agent-Token": token,
        "X-Site-Agent-Timestamp": String(Math.floor(now.getTime() / 1000)),
        "X-Site-Agent-Nonce": randomUUID(),
        "X-Site-Agent-Site-ID": site.pluginSiteId,
        "X-Site-Agent-License-Signature": createHmac("sha256", token).update(body).digest("hex"),
      },
      body,
      cache: "no-store",
      redirect: "error",
      signal: controller.signal,
    });
    if (!response.ok) throw new Error(`WordPress rejected the license update (${response.status}).`);
    const result = await response.json() as { success?: boolean };
    if (!result.success) throw new Error("WordPress did not confirm the license update.");
    await db.site.update({ where: { id: site.id }, data: { licenseActive: active, licenseValidUntil: validUntil, aiChatEntitled: payload.features.ai_chat, status: SiteStatus.CONNECTED, lastConnectionError: null } });
    return validUntil;
  } catch (error) {
    await db.site.update({ where: { id: site.id }, data: { licenseActive: false, licenseValidUntil: null, status: SiteStatus.NEEDS_ATTENTION, lastConnectionError: error instanceof Error ? error.message : "License update failed." } });
    throw error;
  } finally {
    clearTimeout(timeout);
  }
}

export async function syncOrganizationLicenses(organizationId: string) {
  const [subscription, sites] = await Promise.all([
    db.subscription.findUnique({ where: { organizationId }, include: { plan: true } }),
    db.site.findMany({ where: { organizationId, status: { not: SiteStatus.REMOVED } }, orderBy: { createdAt: "asc" } }),
  ]);
  const results = await Promise.allSettled(sites.map((site, index) => syncSiteLicense(site.id, index < (subscription?.plan.siteLimit ?? 0))));
  return { total: sites.length, failed: results.filter((result) => result.status === "rejected").length };
}
