import { createHash } from "crypto";
import { supabaseAdmin as db } from "./supabase-admin";

// Batas usia event yang dikirim ke Meta. Verifikasi nilainya di dokumentasi Meta lalu sesuaikan lewat env.
export const MAX_EVENT_AGE_DAYS = Number(process.env.META_MAX_EVENT_AGE_DAYS ?? 7);

const sha256 = (v: string) => createHash("sha256").update(v.trim().toLowerCase()).digest("hex");

type EventRow = {
  id: string;
  org_id: string;
  source_click_id: string | null;
  lead_id: string | null;
  event_name: string;
  event_id: string;
  event_time: string;
  attempts: number;
  payload: { custom_data?: Record<string, unknown> } | null;
};

/**
 * Kirim satu baris meta_events ke Conversions API, lalu catat hasilnya.
 * Aman dipanggil ulang oleh worker retry untuk status 'pending' atau 'failed'.
 */
export async function sendMetaEvent(eventRowId: string): Promise<void> {
  // Isi dengan versi Graph API terbaru yang didukung Meta (cek dokumentasi Meta), mis. "v2x.0"
  const version = process.env.META_GRAPH_VERSION;
  if (!version) throw new Error("META_GRAPH_VERSION belum diisi");

  const { data: ev } = await db.from("meta_events").select("*").eq("id", eventRowId).single<EventRow>();
  if (!ev) return;

  if (Date.now() - new Date(ev.event_time).getTime() > MAX_EVENT_AGE_DAYS * 864e5) {
    await db
      .from("meta_events")
      .update({ status: "skipped", error: `Lebih tua dari ${MAX_EVENT_AGE_DAYS} hari, tidak dikirim` })
      .eq("id", ev.id);
    return;
  }

  const markFailed = (error: string) =>
    db.from("meta_events").update({ status: "failed", attempts: ev.attempts + 1, error }).eq("id", ev.id);

  const { data: conn } = await db
    .from("meta_connections")
    .select("dataset_id, test_event_code")
    .eq("org_id", ev.org_id)
    .maybeSingle();
  const { data: token } = await db.rpc("get_capi_token", { p_org: ev.org_id });
  if (!conn || !token) {
    await markFailed("Koneksi Meta atau token CAPI belum diatur");
    return;
  }

  // Data pencocokan: dari klik asal, plus nomor WA ter-hash kalau event berasal dari lead
  let phoneSha: string | null = null;
  let clickRowId = ev.source_click_id;
  if (ev.lead_id) {
    const { data: lead } = await db
      .from("leads")
      .select("phone_sha256, source_click_id")
      .eq("id", ev.lead_id)
      .maybeSingle();
    phoneSha = lead?.phone_sha256 ?? null;
    clickRowId = clickRowId ?? lead?.source_click_id ?? null;
  }
  const { data: click } = clickRowId
    ? await db
        .from("clicks")
        .select("click_id, fbc, fbp, client_ip, user_agent, event_source_url")
        .eq("id", clickRowId)
        .maybeSingle()
    : { data: null };

  const user_data: Record<string, unknown> = {};
  if (click?.fbc) user_data.fbc = click.fbc;
  if (click?.fbp) user_data.fbp = click.fbp;
  if (click?.client_ip) user_data.client_ip_address = click.client_ip;
  if (click?.user_agent) user_data.client_user_agent = click.user_agent;
  if (phoneSha) user_data.ph = [phoneSha];
  if (click?.click_id) user_data.external_id = [sha256(click.click_id)];

  const fromChat = Boolean(ev.lead_id);
  const data = {
    event_name: ev.event_name,
    event_time: Math.floor(new Date(ev.event_time).getTime() / 1000),
    event_id: ev.event_id,
    // Lead dari klik LP = "website". Event dari chat = "chat" (cek nilai terbaru di dokumentasi Meta).
    action_source: fromChat ? "chat" : "website",
    ...(!fromChat && click?.event_source_url ? { event_source_url: click.event_source_url } : {}),
    user_data,
    ...(ev.payload?.custom_data ? { custom_data: ev.payload.custom_data } : {}),
  };

  const ctrl = new AbortController();
  const timer = setTimeout(() => ctrl.abort(), 8000);
  try {
    const res = await fetch(`https://graph.facebook.com/${version}/${conn.dataset_id}/events`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      // token di body (bukan di URL) supaya tidak muncul di log
      body: JSON.stringify({
        data: [data],
        access_token: token,
        ...(conn.test_event_code ? { test_event_code: conn.test_event_code } : {}),
      }),
      signal: ctrl.signal,
    });
    const json = await res.json().catch(() => null);
    await db
      .from("meta_events")
      .update({
        status: res.ok ? "sent" : "failed",
        attempts: ev.attempts + 1,
        payload: { ...(ev.payload ?? {}), sent: data },
        response: json,
        error: res.ok ? null : `HTTP ${res.status}`,
        sent_at: res.ok ? new Date().toISOString() : null,
      })
      .eq("id", ev.id);
  } catch (e) {
    await markFailed(e instanceof Error ? e.message : String(e));
  } finally {
    clearTimeout(timer);
  }
}
