import { NextResponse } from "next/server";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import { createAdminClient, createClient } from "@/lib/supabase-server";
import {
  conferirChaveDoAsaas,
  credenciaisAtivas,
  gerarTokenDeWebhook,
  lerConfiguracaoDoAsaas,
  registrarWebhookDoAsaas,
} from "@/lib/asaas";

/*
  A tela de integração do Asaas.

  Gêmea de `/api/integrations/mercado-pago`, com a mesma permissão e o mesmo
  formato de resposta — a tela de Integrações desenha os dois provedores lado a
  lado e não deve precisar de dois vocabulários.

  Três coisas acontecem aqui que não acontecem no lado do Mercado Pago:

    1. A chave é conferida antes de ser gravada. Chave errada guardada em
       silêncio só aparece no primeiro cliente que tenta pagar, e aí o prejuízo
       é a venda. Se a conferência falha, nada é escrito.
    2. O webhook é registrado sozinho na conta do lojista. Ele nunca precisa
       abrir o painel do Asaas.
    3. Os dois provedores convivem. A primeira versão desta rota DESLIGAVA o
       Mercado Pago ao conectar o Asaas, porque sem a engrenagem "quem cobra?"
       não tinha resposta única. Com a escolha por local e por meio, a pergunta
       tem resposta sem tirar nada de ninguém — e conectar o Asaas deixou de ser
       um caminho sem volta.
*/

export const runtime = "nodejs";

function texto(valor: unknown) {
  return String(valor ?? "").trim();
}

/*
  O endereço do aviso. O webhook mora no app da loja, não aqui.

  Sem endereço público — máquina local — o registro é pulado, e a conferência
  ativa de status cobre o desenvolvimento. É o mesmo critério que a cobrança
  Pix do balcão já usa para decidir se manda `notification_url`.
*/
function enderecoDoAviso(integrationId: string) {
  const base = texto(process.env.NEXT_PUBLIC_PUBLIC_CATALOG_BASE_URL || process.env.PUBLIC_CATALOG_BASE_URL).replace(
    /\/$/,
    ""
  );
  if (!base) return null;

  try {
    const host = new URL(base).hostname.toLowerCase();
    if (host === "localhost" || host === "127.0.0.1" || host.endsWith(".localhost")) return null;
    return `${base}/api/webhooks/asaas/${integrationId}`;
  } catch {
    return null;
  }
}

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, supabase, 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();

  const [{ data: integracao, error: erroDaIntegracao }, { data: contas, error: erroDasContas }] = await Promise.all([
    admin
      .from("integrations")
      .select("id,active,settings,updated_at")
      .eq("provider", "asaas")
      .eq("user_id", ownerId)
      .maybeSingle(),
    admin
      .from("bank_accounts")
      .select("id,name,bank_name,bank_code,active")
      .eq("company_id", a.ctx.companyId)
      .eq("active", true)
      .order("name", { ascending: true }),
  ]);

  if (erroDaIntegracao) {
    return NextResponse.json({ error: erroDaIntegracao.message || "Erro ao carregar integração." }, { status: 500 });
  }
  if (erroDasContas) {
    return NextResponse.json({ error: erroDasContas.message || "Erro ao carregar contas bancárias." }, { status: 500 });
  }

  const bruto = (integracao?.settings || {}) as Record<string, unknown>;
  const config = lerConfiguracaoDoAsaas(bruto);

  return NextResponse.json({
    ok: true,
    config: {
      id: integracao?.id || null,
      active: Boolean(integracao?.id) && integracao?.active !== false,
      updated_at: integracao?.updated_at || null,
      ...config,
      /* A chave nunca volta inteira para a tela. O que volta é a marca de que
         existe uma, para o campo poder dizer "já configurada" sem reexibir o
         segredo a cada abertura da página. */
      sandbox_api_key: "",
      production_api_key: "",
      tem_chave_sandbox: Boolean(config.sandbox_api_key),
      tem_chave_producao: Boolean(config.production_api_key),
      conta_nome: texto(bruto.conta_nome) || null,
      conta_documento: texto(bruto.conta_documento) || null,
      webhook_url: texto(bruto.webhook_url) || null,
    },
    bank_accounts: (contas || []).map((conta) => ({
      id: conta.id,
      name: conta.bank_name ? `${conta.name} - ${conta.bank_name}` : conta.name,
      bank_name: conta.bank_name || null,
      bank_code: conta.bank_code || null,
    })),
  });
}

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 Record<string, unknown> | null;
  const querLigar = corpo?.active !== false;

  const { data: existente } = await admin
    .from("integrations")
    .select("id,settings")
    .eq("provider", "asaas")
    .eq("user_id", ownerId)
    .maybeSingle();

  const guardado = (existente?.settings || {}) as Record<string, unknown>;
  const config = lerConfiguracaoDoAsaas(corpo);

  /* Campo de senha em branco quer dizer "não mexi", e não "apague". Sem isso,
     abrir a tela e salvar o vencimento apagaria a chave de produção. */
  if (!config.sandbox_api_key) config.sandbox_api_key = texto(guardado.sandbox_api_key);
  if (!config.production_api_key) config.production_api_key = texto(guardado.production_api_key);

  /* O segredo do webhook também não vem da tela: ele é nosso, nasce uma vez e
     sobrevive a desligar e religar. Trocar a cada gravação invalidaria os
     avisos em voo, que chegariam com o token velho. */
  config.webhook_token = texto(guardado.webhook_token);

  if (
    config.bank_account_id &&
    !(
      await admin
        .from("bank_accounts")
        .select("id")
        .eq("id", config.bank_account_id)
        .eq("company_id", a.ctx.companyId)
        .eq("active", true)
        .maybeSingle()
    ).data?.id
  ) {
    return NextResponse.json({ error: "Conta bancária inválida." }, { status: 400 });
  }

  /* ------------------------------------------------------ desligar é fácil */

  if (!querLigar) {
    if (!existente?.id) return NextResponse.json({ ok: true });

    const { error } = await admin
      .from("integrations")
      .update({ active: false, settings: { ...guardado, ...config }, updated_at: new Date().toISOString() })
      .eq("id", existente.id);

    if (error) return NextResponse.json({ error: error.message || "Erro ao salvar integração." }, { status: 500 });

    /* O Mercado Pago não é tocado aqui: ele nunca foi desligado para o Asaas
       entrar. Desconectar o Asaas só faz as escolhas que apontavam para ele
       caírem no automático, e o automático sobra o Mercado Pago. */
    return NextResponse.json({ ok: true, active: false });
  }

  /* -------------------------------------------------------- ligar dá trabalho */

  const credenciais = credenciaisAtivas(config);
  if (!credenciais) {
    /* "Chave de API" e o nome que o Asaas usa no painel dele, e e o que o
       lojista vai procurar. Dizer "de sandbox" aqui so faria sentido para
       quem desenvolve, e essa pessoa ja sabe. */
    return NextResponse.json({ error: "Informe a chave de API do Asaas." }, { status: 400 });
  }

  let conta: Awaited<ReturnType<typeof conferirChaveDoAsaas>>;
  try {
    conta = await conferirChaveDoAsaas(credenciais);
  } catch (erro) {
    /* Nada foi escrito até aqui, de propósito: o lojista continua com a
       integração que tinha antes, em vez de ficar sem nenhuma. */
    const motivo = erro instanceof Error ? erro.message : "Não foi possível falar com o Asaas.";
    return NextResponse.json({ error: `A chave não foi aceita pelo Asaas. ${motivo}` }, { status: 400 });
  }

  if (!config.webhook_token) config.webhook_token = gerarTokenDeWebhook();

  /* A linha precisa existir antes do registro do webhook, porque o endereço do
     aviso carrega o id dela. */
  let integrationId = texto(existente?.id);
  if (!integrationId) {
    const { data: criada, error } = await admin
      .from("integrations")
      .insert({ user_id: ownerId, provider: "asaas", token: null, refresh_token: null, settings: config, active: true })
      .select("id")
      .single();

    if (error || !criada?.id) {
      return NextResponse.json({ error: error?.message || "Erro ao criar integração." }, { status: 500 });
    }
    integrationId = String(criada.id);
  }

  const urlDoAviso = enderecoDoAviso(integrationId);
  let avisoRegistrado = false;
  let avisoFalhou: string | null = null;

  if (urlDoAviso) {
    try {
      await registrarWebhookDoAsaas(credenciais, {
        url: urlDoAviso,
        token: config.webhook_token,
        email: texto(conta.email) || texto(a.user.email) || "suporte@ktalogu.com.br",
      });
      avisoRegistrado = true;
    } catch (erro) {
      /* Webhook é otimização, não requisito: a tela do pedido e o balcão
         conferem o status sozinhos. Então a integração é salva mesmo assim, e
         a tela avisa o que faltou em vez de fingir que deu tudo certo. */
      avisoFalhou = erro instanceof Error ? erro.message : "Não foi possível registrar o aviso automático.";
    }
  }

  /* O nome da conta vem do Asaas, nunca do corpo da requisição: é o que o
     lojista lê na tela para conferir que conectou a conta certa, e não a do
     sócio ou a de sandbox achando que era produção. */
  const paraGravar: Record<string, unknown> = {
    ...config,
    conta_nome: conta.nome,
    conta_documento: conta.documento,
    conta_email: conta.email,
    webhook_url: avisoRegistrado ? urlDoAviso : null,
  };

  const { error: erroAoSalvar } = await admin
    .from("integrations")
    .update({ active: true, settings: paraGravar, updated_at: new Date().toISOString() })
    .eq("id", integrationId);

  if (erroAoSalvar) {
    return NextResponse.json({ error: erroAoSalvar.message || "Erro ao salvar integração." }, { status: 500 });
  }

  return NextResponse.json({
    ok: true,
    active: true,
    conta: { nome: conta.nome, documento: conta.documento },
    webhook: { registrado: avisoRegistrado, url: urlDoAviso, erro: avisoFalhou },
  });
}
