import { NextResponse } from "next/server";

import { origemDoPedido, redeDoIp } from "@/lib/digitais/servidor/acessos";
import { aparelhoValido, sinalDoAparelho, soltarAparelho } from "@/lib/digitais/servidor/aparelhos";
import { emitirCertificadoSeConcluiu } from "@/lib/digitais/servidor/certificados";
import { lerAjustesDigitais } from "@/lib/digitais/servidor/vitrine";
import { acessoAAula } from "@/lib/digitais/servidor/curso";
import { createAdminClient } from "@/lib/supabase-admin";

/*
 * O progresso de uma aula: onde o aluno parou (a cada 10 s e ao sair, pelo
 * `sendBeacon`), o quanto viu de fato, e a marca de "concluída" (sozinha, ao
 * passar da porcentagem das Configurações, ou pelo botão). A prévia da equipe
 * não grava nada. Concluiu a última aula: o certificado nasce (e a tela avisa).
 *
 * É também o sinal do aparelho que está tocando (`dispositivo`): passou do
 * limite de aparelhos ao mesmo tempo, a resposta diz `parar` para o que
 * começou primeiro.
 */

export async function POST(request: Request) {
  const semCache = { headers: { "cache-control": "no-store" } };
  try {
    /* O `sendBeacon` manda texto: o corpo é lido como texto e depois JSON. */
    const bruto = await request.text().catch(() => "");
    let corpo: Record<string, unknown> | null = null;
    try {
      corpo = bruto ? (JSON.parse(bruto) as Record<string, unknown>) : null;
    } catch {
      corpo = null;
    }
    const slug = typeof corpo?.slug === "string" ? corpo.slug.trim().toLowerCase() : "";
    const aulaId = typeof corpo?.aulaId === "string" ? corpo.aulaId : "";
    if (!slug || !aulaId) return NextResponse.json({ error: "Progresso inválido." }, { status: 400, ...semCache });

    const admin = createAdminClient();
    const acesso = await acessoAAula(admin, { slug, aulaId });
    if (!acesso.ok) return NextResponse.json({ error: acesso.motivo }, { status: acesso.status, ...semCache });
    if (acesso.quem.tipo !== "aluno") return NextResponse.json({ ok: true, previa: true }, semCache);
    const { aula, quem, lojaId } = acesso;

    const numero = (valor: unknown) => {
      const n = Math.round(Number(valor));
      return Number.isFinite(n) && n >= 0 ? Math.min(86400, n) : null;
    };
    const posicao = numero(corpo?.posicao) ?? 0;
    const duracao = numero(corpo?.duracao) ?? aula.duracao_s ?? null;
    const ajustes = await lerAjustesDigitais(admin, lojaId);

    /* O aparelho: tocando dá sinal; pausado ou saindo, deixa de contar. */
    let parar = false;
    const dispositivo = aparelhoValido(corpo?.dispositivo);
    if (dispositivo) {
      if (corpo?.tocando === true) {
        const origem = origemDoPedido(request);
        parar = await sinalDoAparelho(admin, {
          lojaId,
          clienteId: quem.clienteId,
          dispositivo,
          aulaId: aula.id,
          limite: ajustes.protecao.aparelhos,
          assumir: corpo?.assumir === true,
          rede: redeDoIp(origem.ip),
          navegador: origem.navegador,
        }).catch(() => false);
      } else if (corpo?.tocando === false) {
        await soltarAparelho(admin, { lojaId, clienteId: quem.clienteId, dispositivo }).catch(() => undefined);
      }
    }

    const { data: atual } = await admin
      .from("digital_course_progress")
      .select("assistido_s,concluida_em")
      .eq("client_id", quem.clienteId)
      .eq("lesson_id", aula.id)
      .maybeSingle();
    const anterior = atual as { assistido_s: number; concluida_em: string | null } | null;
    let assistido = Math.max(anterior?.assistido_s ?? 0, posicao);

    let concluidaEm = anterior?.concluida_em ?? null;
    if (corpo?.concluir === true) concluidaEm = concluidaEm ?? new Date().toISOString();
    else if (corpo?.concluir === false) {
      /* Desmarcou: a conta do "assistido" recomeça daqui, senão a próxima
         gravação marcaria de novo na hora. */
      concluidaEm = null;
      assistido = posicao;
    } else if (!concluidaEm && duracao && duracao > 0) {
      if ((assistido / duracao) * 100 >= ajustes.player.percentualAssistida) concluidaEm = new Date().toISOString();
    }

    const agora = new Date().toISOString();
    const { error } = await admin.from("digital_course_progress").upsert(
      {
        user_id: lojaId,
        client_id: quem.clienteId,
        product_id: aula.product_id,
        lesson_id: aula.id,
        posicao_s: posicao,
        assistido_s: assistido,
        duracao_s: duracao,
        concluida_em: concluidaEm,
        atualizado_em: agora,
      },
      { onConflict: "client_id,lesson_id" }
    );
    if (error) return NextResponse.json({ error: "Não foi possível guardar o progresso." }, { status: 500, ...semCache });

    /* O "último acesso" do aluno no painel (Alunos e acessos). */
    await admin
      .from("digital_entitlements")
      .update({ ultimo_acesso_em: agora })
      .eq("user_id", lojaId)
      .eq("client_id", quem.clienteId)
      .eq("product_id", aula.product_id);

    /* Acabou de concluir: se era a última, o certificado nasce agora. Falhar
       aqui não perde o progresso (a área de membros tenta de novo). */
    let certificado: { codigo: string } | null = null;
    if (concluidaEm && !anterior?.concluida_em) {
      const emitido = await emitirCertificadoSeConcluiu(admin, {
        lojaId,
        clienteId: quem.clienteId,
        produtoId: aula.product_id,
        inicio: quem.inicio,
      }).catch((erro) => {
        console.error("[digitais] certificado ao concluir", erro);
        return null;
      });
      if (emitido?.novo) certificado = { codigo: emitido.codigo };
    }

    return NextResponse.json({ ok: true, concluida: Boolean(concluidaEm), assistido, certificado, parar }, semCache);
  } catch (erro) {
    console.error("[digitais] progresso", erro);
    return NextResponse.json({ error: "Não foi possível guardar o progresso." }, { status: 500, ...semCache });
  }
}
