import { after } from "next/server";
import { supabaseAdmin as admin } from "./supabase-admin";
import { sendMetaEvent, MAX_EVENT_AGE_DAYS } from "./capi";
import { RANK, STAGE_EVENT, type Stage } from "./stage-defs";

export type ApplyArgs = {
  orgId: string;
  leadId: string;
  stage: Stage;
  occurredAt: Date;
  source: "cs" | "ai_suggested" | "ai_auto" | "system";
  confidence?: number;
  userId?: string;
  note?: string;
  orderId?: string;
  customData?: Record<string, unknown>;
};

/**
 * Mencatat satu tahap funnel pada lead, lalu mengantrekan event Meta yang bersesuaian.
 * Idempoten: tiap tahap hanya sekali per lead (kecuali "purchased" yang bisa repeat order).
 * Mengembalikan false kalau tahap itu sudah pernah tercatat.
 */
export async function applyStage(a: ApplyArgs): Promise<boolean> {
  const { data: lead } = await admin.from("leads").select("id, stage").eq("id", a.leadId).eq("org_id", a.orgId).maybeSingle();
  if (!lead) throw new Error("Lead tidak ditemukan");

  const { data: hist } = await admin.from("lead_stage_history").select("stage").eq("lead_id", a.leadId);
  const have = new Set<string>((hist ?? []).map((h: { stage: string }) => h.stage));
  if (a.stage !== "purchased" && have.has(a.stage)) return false;

  // event_time tidak boleh di masa depan
  const when = new Date(Math.min(a.occurredAt.getTime(), Date.now()));

  // Opsi per perusahaan: isi tahap yang terlewat (default mati supaya data jujur)
  const todo: Stage[] = [];
  if (a.stage !== "lost" && RANK[a.stage] > 1) {
    const { data: conn } = await admin.from("meta_connections").select("backfill_missed_stages").eq("org_id", a.orgId).maybeSingle();
    if (conn?.backfill_missed_stages) {
      for (const s of ["contacted", "product_selected", "checkout", "payment_info"] as Stage[]) {
        if (RANK[s] < RANK[a.stage] && !have.has(s)) todo.push(s);
      }
    }
  }
  todo.push(a.stage);

  for (const s of todo) {
    const own = s === a.stage;
    const { error } = await admin.from("lead_stage_history").insert({
      org_id: a.orgId,
      lead_id: a.leadId,
      stage: s,
      occurred_at: when.toISOString(),
      source: own ? a.source : "system",
      confidence: own ? a.confidence ?? null : null,
      changed_by: a.userId ?? null,
      note: own ? a.note ?? null : "backfill",
    });
    if (error) throw error;
    await enqueue(a, s, when, own);
  }

  const patch: Record<string, unknown> = {};
  if (a.stage === "lost") {
    if (lead.stage !== "purchased") patch.stage = "lost";
  } else if (lead.stage === "lost" || RANK[a.stage] > RANK[lead.stage as Stage]) {
    patch.stage = a.stage;
  }
  if (a.stage === "purchased") patch.closed_at = when.toISOString();
  if (Object.keys(patch).length) await admin.from("leads").update(patch).eq("id", a.leadId);
  return true;
}

async function enqueue(a: ApplyArgs, stage: Stage, when: Date, own: boolean) {
  const name = STAGE_EVENT[stage];
  if (!name) return;

  const eventId = stage === "purchased" ? `purchase_${a.orderId}` : `${name.toLowerCase()}_${a.leadId}`;
  const tooOld = Date.now() - when.getTime() > MAX_EVENT_AGE_DAYS * 864e5;

  const { data, error } = await admin
    .from("meta_events")
    .insert({
      org_id: a.orgId,
      lead_id: a.leadId,
      order_id: own ? a.orderId ?? null : null,
      event_name: name,
      event_id: eventId,
      event_time: when.toISOString(),
      status: tooOld ? "skipped" : "pending",
      error: tooOld ? `Lebih tua dari ${MAX_EVENT_AGE_DAYS} hari, tidak dikirim` : null,
      payload: own && a.customData ? { custom_data: a.customData } : null,
    })
    .select("id")
    .single();

  if (error) {
    if (error.code === "23505") return; // event ini sudah pernah dibuat
    throw error;
  }
  if (data && !tooOld) after(() => sendMetaEvent(data.id).catch((e) => console.error(`CAPI ${name} gagal`, e)));
}
