import { NextResponse } from "next/server";
import { createAdminClient, createClient } from "@/lib/supabase-server";
import { getCompanyContext } from "@/lib/company-context";

/**
 * O popup que esta pessoa tem de ver agora, se houver.
 *
 * Quem decide é o servidor, e não a tela: a regra envolve a empresa de quem
 * abriu, o período do aviso e o que essa pessoa já fechou antes. Mandar tudo
 * para o navegador filtrar entregaria de brinde o texto de avisos que não são
 * dela.
 */

export const runtime = "nodejs";
export const dynamic = "force-dynamic";

type PopupRow = {
  id: string;
  title: string;
  message: string | null;
  image_url: string | null;
  image_url_mobile: string | null;
  image_side: string;
  background_color: string;
  text_color: string;
  cta_label: string | null;
  cta_url: string | null;
  cta_new_tab: boolean;
  require_ack: boolean;
  ack_label: string | null;
  frequency: string;
  audience: string;
  start_at: string | null;
  end_at: string | null;
  created_at: string;
  updated_at: string;
};

const CAMPOS =
  "id,title,message,image_url,image_url_mobile,image_side,background_color,text_color,cta_label,cta_url,cta_new_tab,require_ack,ack_label,frequency,audience,start_at,end_at,created_at,updated_at";

/* O texto padrao da caixa de ciencia, quando o cadastro nao escreveu um.
   Sem `export`: um arquivo de rota so pode exportar o que o Next reconhece
   (os verbos HTTP, `runtime`, `dynamic` e companhia). Qualquer outro nome
   derruba o build com "is not a valid Route export field" — e passa batido
   no `tsc`, porque quem confere isso e o proprio `next build`. */
const CIENCIA_PADRAO = "Li e estou ciente deste aviso.";

/** O dia de hoje no fuso de Brasília, que é o dia da gráfica. */
function diaDeHoje(quando: Date) {
  return new Intl.DateTimeFormat("en-CA", { timeZone: "America/Sao_Paulo" }).format(quando);
}

export async function GET() {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();
  if (!user) return NextResponse.json({ popups: [] });

  const ctx = await getCompanyContext(supabase, user);
  const companyId = ctx?.companyId ? String(ctx.companyId) : "";

  const admin = createAdminClient();
  const { data, error } = await admin
    .from("system_popups")
    .select(CAMPOS)
    .eq("active", true)
    .order("created_at", { ascending: true })
    .limit(20);

  /* Popup é recado, não é o sistema. Se a consulta falhar, a tela abre sem ele
     em vez de abrir quebrada. */
  if (error) return NextResponse.json({ popups: [] });

  const agora = Date.now();
  const noPrazo = (Array.isArray(data) ? (data as PopupRow[]) : []).filter((popup) => {
    const inicio = popup.start_at ? new Date(popup.start_at).getTime() : null;
    const fim = popup.end_at ? new Date(popup.end_at).getTime() : null;
    if (inicio && Number.isFinite(inicio) && agora < inicio) return false;
    if (fim && Number.isFinite(fim) && agora > fim) return false;
    return true;
  });

  if (noPrazo.length === 0) return NextResponse.json({ popups: [] });

  /* Empresa escolhida a dedo: só passa quem está na lista do popup. Sem
     empresa no contexto, só sobram os de público geral. */
  const escolhidos = noPrazo.filter((popup) => popup.audience === "selected").map((popup) => popup.id);
  const empresasPorPopup = new Set<string>();
  if (escolhidos.length > 0 && companyId) {
    const { data: vinculos } = await admin
      .from("system_popup_companies")
      .select("popup_id")
      .eq("company_id", companyId)
      .in("popup_id", escolhidos);
    for (const vinculo of vinculos ?? []) empresasPorPopup.add(String(vinculo.popup_id));
  }

  const paraEstaEmpresa = noPrazo.filter(
    (popup) => popup.audience !== "selected" || empresasPorPopup.has(popup.id)
  );
  if (paraEstaEmpresa.length === 0) return NextResponse.json({ popups: [] });

  const { data: vistos } = await admin
    .from("system_popup_views")
    .select("popup_id,opened_at,seen_at,acknowledged_at,popup_version")
    .eq("user_id", user.id)
    .in(
      "popup_id",
      paraEstaEmpresa.map((popup) => popup.id)
    );

  const visto = new Map((vistos ?? []).map((linha) => [String(linha.popup_id), linha]));
  const hoje = diaDeHoje(new Date());

  const aMostrar = paraEstaEmpresa.filter((popup) => {
    if (popup.frequency === "always") return true;

    const jaViu = visto.get(popup.id);
    if (!jaViu) return true;

    /* "Uma vez por pessoa" conta a partir da versão: editar o popup carimba um
       `updated_at` novo e o aviso volta, que é como se corrige um texto errado
       sem ter de criar outro. */
    const versaoVista = new Date(String(jaViu.popup_version)).getTime();
    const versaoAtual = new Date(popup.updated_at).getTime();
    if (Number.isFinite(versaoVista) && Number.isFinite(versaoAtual) && versaoVista < versaoAtual) return true;

    /* Abriu e não resolveu: volta na próxima entrada. Com ciência obrigatória,
       só a ciência resolve; sem ela, fechar já basta. Sem isso, quem fechasse
       a aba no meio do aviso nunca mais o veria. */
    const resolveu = popup.require_ack === true ? Boolean(jaViu.acknowledged_at) : Boolean(jaViu.seen_at);
    if (!resolveu) return true;

    if (popup.frequency === "daily") {
      const quando = jaViu.seen_at || jaViu.opened_at;
      return quando ? diaDeHoje(new Date(String(quando))) !== hoje : true;
    }

    return false;
  });

  return NextResponse.json({
    popups: aMostrar.map((popup) => ({
      id: popup.id,
      title: popup.title,
      message: popup.message,
      image_url: popup.image_url,
      image_url_mobile: popup.image_url_mobile,
      image_side: popup.image_side === "right" ? "right" : "left",
      background_color: popup.background_color,
      text_color: popup.text_color,
      cta_label: popup.cta_label,
      cta_url: popup.cta_url,
      cta_new_tab: popup.cta_new_tab !== false,
      require_ack: popup.require_ack === true,
      ack_label: popup.ack_label || CIENCIA_PADRAO,
      version: popup.updated_at,
    })),
  });
}

/**
 * Os tres momentos do aviso.
 *
 *   abriu   a janela apareceu na frente da pessoa
 *   fechou  ela saiu, no X ou clicando fora
 *   ciente  ela marcou a caixa de ciencia
 *
 * Sao tres e nao um porque a metrica precisa separar "viu e nao fez nada" de
 * "nem chegou a ver". Com um registro so, os dois seriam iguais.
 */
export async function POST(request: Request) {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();
  if (!user) return NextResponse.json({ error: "Não autenticado." }, { status: 401 });

  const body = (await request.json().catch(() => null)) as { popup_id?: unknown; acao?: unknown } | null;
  const popupId = typeof body?.popup_id === "string" ? body.popup_id.trim() : "";
  const acaoBruta = typeof body?.acao === "string" ? body.acao.trim().toLowerCase() : "fechou";
  const acao = acaoBruta === "abriu" || acaoBruta === "ciente" ? acaoBruta : "fechou";
  if (!popupId) return NextResponse.json({ error: "Popup inválido." }, { status: 400 });

  const ctx = await getCompanyContext(supabase, user);
  const companyId = ctx?.companyId ? String(ctx.companyId) : null;

  const admin = createAdminClient();
  /* A versao e o texto da ciencia vem do banco, e nao do navegador. Se
     viessem de fora, bastava mandar uma data futura para nunca mais ver aviso
     nenhum, ou inventar o que a pessoa teria concordado. */
  const { data: popup } = await admin
    .from("system_popups")
    .select("updated_at,ack_label")
    .eq("id", popupId)
    .maybeSingle();
  if (!popup) return NextResponse.json({ error: "Popup não encontrado." }, { status: 404 });

  const agora = new Date().toISOString();
  const linha: Record<string, unknown> = {
    popup_id: popupId,
    user_id: user.id,
    company_id: companyId,
    popup_version: popup.updated_at,
  };

  if (acao === "abriu") {
    linha.opened_at = agora;
    /* Reabriu depois de uma edicao: a saida e a ciencia anteriores nao valem
       mais para esta versao do texto. */
    linha.seen_at = null;
    linha.acknowledged_at = null;
    linha.ack_label = null;
  } else if (acao === "ciente") {
    linha.acknowledged_at = agora;
    linha.seen_at = agora;
    linha.ack_label = popup.ack_label || CIENCIA_PADRAO;
  } else {
    linha.seen_at = agora;
  }

  const { error } = await admin.from("system_popup_views").upsert(linha, { onConflict: "popup_id,user_id" });
  if (error) return NextResponse.json({ error: "Não foi possível registrar o popup." }, { status: 400 });
  return NextResponse.json({ ok: true });
}
