import { NextResponse } from "next/server";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import { createAdminClient, createClient } from "@/lib/supabase-server";
import {
  EXPLICACAO_DO_LOCAL,
  LOCAIS,
  ROTULO_DO_LOCAL,
  ROTULO_DO_MEIO,
  ROTULO_DO_PROVEDOR,
  carregarPagamento,
  ehProvedorConhecido,
  integracaoServeParaCobrar,
  meiosPossiveis,
  porQueNaoTem,
  provedorDoMeio,
  provedorSabe,
  type Local,
  type Meio,
  type NomeDoProvedor,
} from "@/lib/provedor-de-pagamento";

/*
  A engrenagem: quem recebe o quê, e onde.

  Devolve a grade inteira pronta para desenhar. Cada célula já vem sabendo três
  coisas: quais provedores podem cobrar aquilo, qual está cobrando agora, e se
  isso foi escolha do lojista ou o automático. A tela não recalcula nada — se
  recalculasse, existiriam duas regras, e um dia elas discordariam.

  Gravar substitui o conjunto inteiro de escolhas. São poucas linhas, e trocar
  tudo de uma vez evita o estado meio-gravado que um envio parcial deixaria.
*/

export const runtime = "nodejs";

type CorpoDoEnvio = { rotas?: Array<{ surface?: unknown; method?: unknown; provider?: unknown }> };

async function acesso() {
  const supabase = await createClient();
  const {
    data: { user },
    error,
  } = await supabase.auth.getUser();

  if (error || !user) {
    return { ok: false as const, resposta: NextResponse.json({ error: "Não autenticado." }, { status: 401 }) };
  }

  const ctx = await getCompanyContext(supabase, user);
  if (!canCompany(ctx, user.id, "financial.bank.integrations")) {
    return { ok: false as const, resposta: NextResponse.json({ error: "Sem permissão." }, { status: 403 }) };
  }

  return { ok: true as const, user, ctx };
}

export async function GET() {
  const a = await acesso();
  if (!a.ok) return a.resposta;

  const ownerId = a.ctx.ownerId || a.user.id;
  const admin = createAdminClient();

  let estado: Awaited<ReturnType<typeof carregarPagamento>>;
  try {
    estado = await carregarPagamento(admin as never, ownerId);
  } catch (erro) {
    return NextResponse.json(
      { error: erro instanceof Error ? erro.message : "Erro ao carregar os provedores." },
      { status: 500 }
    );
  }

  const conectados = estado.integracoes.filter((linha) => ehProvedorConhecido(linha.provider) && linha.active !== false);

  const provedores = (["mercado_pago", "asaas"] as NomeDoProvedor[]).map((nome) => {
    const linha = conectados.find((item) => item.provider === nome) ?? null;
    const conta = (linha?.settings || {}) as Record<string, unknown>;
    return {
      nome,
      rotulo: ROTULO_DO_PROVEDOR[nome],
      conectado: Boolean(linha),
      /* Só o Asaas guarda o nome da conta conectada; o Mercado Pago nunca
         devolveu isso na integração. */
      conta: String(conta.conta_nome || "").trim() || null,
    };
  });

  const locais = LOCAIS.map((local) => ({
    local,
    rotulo: ROTULO_DO_LOCAL[local],
    explicacao: EXPLICACAO_DO_LOCAL[local],
    meios: meiosPossiveis(local).map((meio) => {
      /* Quem pode cobrar isto aqui: sabe fazer, está conectado e completo. */
      const opcoes = (["mercado_pago", "asaas"] as NomeDoProvedor[])
        .filter((nome) => provedorSabe(nome, local, meio))
        .map((nome) => {
          const linha = conectados.find((item) => item.provider === nome) ?? null;
          return {
            provedor: nome,
            rotulo: ROTULO_DO_PROVEDOR[nome],
            disponivel: Boolean(linha) && integracaoServeParaCobrar(linha, local),
          };
        });

      const escolhaGravada = estado.rotas.find((r) => r.surface === local && r.method === meio)?.provider ?? null;
      const ativo = provedorDoMeio(estado, local, meio);

      return {
        meio,
        rotulo: ROTULO_DO_MEIO[meio],
        opcoes,
        /* O que está valendo de verdade neste instante. */
        ativo: ativo?.provedor ?? null,
        /* O que o lojista escolheu, se escolheu. Nulo é "automático". */
        escolhido: escolhaGravada,
        /* A escolha aponta para alguém que não pode cobrar agora? A tela avisa
           em vez de deixar o lojista achando que está mandando no que não está. */
        escolhaIgnorada: Boolean(escolhaGravada && ativo && escolhaGravada !== ativo.provedor),
        indisponiveis: (["mercado_pago", "asaas"] as NomeDoProvedor[])
          .map((nome) => ({ provedor: nome, motivo: porQueNaoTem(nome, local, meio) }))
          .filter((item) => item.motivo),
      };
    }),
  }));

  return NextResponse.json({ ok: true, provedores, locais });
}

export async function PUT(request: Request) {
  const a = await acesso();
  if (!a.ok) return a.resposta;

  const ownerId = a.ctx.ownerId || a.user.id;
  const admin = createAdminClient();
  const corpo = (await request.json().catch(() => null)) as CorpoDoEnvio | null;

  /*
    Só entra o que faz sentido.

    Uma escolha para uma combinação que nenhum provedor faz — cartão pelo Asaas,
    boleto no balcão — não é erro do lojista, é lixo de tela velha ou de chamada
    direta. Ela é descartada em silêncio, e o que sobra é gravado: recusar o
    envio inteiro por causa de uma linha morta perderia as escolhas boas.
  */
  const limpas: Array<{ user_id: string; surface: Local; method: Meio; provider: NomeDoProvedor }> = [];
  for (const bruta of corpo?.rotas || []) {
    const surface = String(bruta?.surface || "") as Local;
    const method = String(bruta?.method || "") as Meio;
    const provider = String(bruta?.provider || "");

    if (!LOCAIS.includes(surface)) continue;
    if (!meiosPossiveis(surface).includes(method)) continue;
    if (!ehProvedorConhecido(provider)) continue;
    if (!provedorSabe(provider, surface, method)) continue;
    if (limpas.some((j) => j.surface === surface && j.method === method)) continue;

    limpas.push({ user_id: ownerId, surface, method, provider });
  }

  /* Trocar o conjunto inteiro: apaga o que existia e grava o que veio. Sem
     linha gravada, aquele par volta para o automático — que é exatamente o que
     "Automático" na tela significa. */
  const { error: erroAoLimpar } = await admin.from("payment_routing").delete().eq("user_id", ownerId);
  if (erroAoLimpar) {
    return NextResponse.json({ error: erroAoLimpar.message || "Erro ao salvar as escolhas." }, { status: 500 });
  }

  if (limpas.length > 0) {
    const { error } = await admin
      .from("payment_routing")
      .insert(limpas.map((linha) => ({ ...linha, updated_at: new Date().toISOString() })));
    if (error) {
      return NextResponse.json({ error: error.message || "Erro ao salvar as escolhas." }, { status: 500 });
    }
  }

  return NextResponse.json({ ok: true, gravadas: limpas.length });
}
