import { NextResponse } from "next/server";
import { createAdminClient, createClient } from "@/lib/supabase-server";
import { canCompany, canManageFinancialCritical, canReadFinancialLookup, getCompanyContext } from "@/lib/company-context";
import {
  getBankAccountDependencies,
  getBankAccountSnapshot,
  logBankAccountAuditEvent,
  executeHardDeleteOperations,
} from "@/lib/bank-account-admin";

const writableFields = new Set([
  "name",
  "bank_name",
  "bank_code",
  "type",
  "initial_balance",
  "agency_number",
  "agency_digit",
  "account_number",
  "account_digit",
  "pix_key",
  "logo_key",
  "active",
]);

type BankAccountRow = {
  id?: string | null;
  bank_account_id?: string | null;
  name?: string | null;
  bank_name?: string | null;
  bank_code?: string | null;
  agency_number?: string | null;
  agency_digit?: string | null;
  account_number?: string | null;
  account_digit?: string | null;
  pix_key?: string | null;
  logo_key?: string | null;
  type?: string | null;
  initial_balance?: number | null;
  active?: boolean | null;
};

type BankAccountBalanceRow = {
  bank_account_id: string;
  flow_balance: number | null;
  current_balance: number | null;
};

type BankAccountBaseRow = {
  id?: string | null;
  bank_account_id?: string | null;
  name: string | null;
  bank_name: string | null;
  bank_code: string | null;
  agency_number: string | null;
  agency_digit: string | null;
  account_number: string | null;
  account_digit: string | null;
  pix_key: string | null;
  logo_key: string | null;
  type: string | null;
  initial_balance: number | null;
  active: boolean | null;
};

function pickWritablePatch(value: unknown) {
  if (!value || typeof value !== "object" || Array.isArray(value)) return {};
  const record = value as Record<string, unknown>;
  const patch: Record<string, unknown> = {};
  for (const [key, v] of Object.entries(record)) {
    if (writableFields.has(key)) patch[key] = v;
  }
  return patch;
}

function normalizeAccountType(value: unknown) {
  if (value === "corrente" || value === "CONTA_CORRENTE") return "CONTA_CORRENTE";
  if (value === "poupanca" || value === "CONTA_POUPANCA") return "CONTA_POUPANCA";
  if (value === "caixa" || value === "CAIXA") return "CAIXA";
  return null;
}

function getCashAccountDefaults(name: string) {
  const normalizedName = name.trim() || "Caixa físico";
  return {
    bank_name: "Caixa físico",
    bank_code: "CXA",
    agency_number: "0000",
    account_number: normalizedName.toUpperCase().replace(/[^A-Z0-9]+/g, "").slice(0, 24) || "CAIXA",
  };
}

function normalizeActive(value: unknown) {
  if (typeof value === "boolean") return value;
  if (typeof value === "string") {
    if (value === "ativa" || value === "true" || value === "1") return true;
    if (value === "inativa" || value === "false" || value === "0") return false;
  }
  if (typeof value === "number") {
    if (value === 1) return true;
    if (value === 0) return false;
  }
  return null;
}

export async function GET() {
  // 1. Autenticar usuário
  const supabase = await createClient();
  const {
    data: { user },
    error: userError,
  } = await supabase.auth.getUser();

  if (userError || !user) {
    return NextResponse.json({ error: "Não autenticado." }, { status: 401 });
  }

  // 2. Obter company_id do contexto e validar permissão
  const ctx = await getCompanyContext(supabase, user);
  /* Consulta de apoio: quem pode ler qualquer tela do Financeiro pode ver
     esta lista. Exigir a chave da tela propria esvaziava as telas vizinhas
     que dependem dela — ver `canReadFinancialLookup`. */
  if (!canReadFinancialLookup(ctx, user.id) && !canCompany(ctx, user.id, "suppliers.read")) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }

  /* Lê com o cliente de serviço, como contas a pagar e fornecedores já leem.
     A política desta tabela pergunta pela chave do módulo, e quem recebeu só a
     chave desta tela via a lista chegar vazia. Quem separa as empresas aqui é
     o `.eq("company_id", ...)` abaixo, não o RLS. */
  const leitura = createAdminClient();

  const { data: baseAccounts, error: baseError } = await leitura
    .from("bank_accounts")
    .select(
      "id,name,bank_code,bank_name,type,initial_balance,active,company_id,agency_number,agency_digit,account_number,account_digit,pix_key,logo_key"
    )
    .eq("company_id", ctx.companyId)
    .order("name", { ascending: true });

  if (baseError) {
    return NextResponse.json(
      {
        error: "Falha ao listar contas bancárias.",
        message: baseError.message,
        details: baseError.details,
        hint: baseError.hint,
        code: baseError.code,
      },
      { status: 500 }
    );
  }

  let balances: BankAccountBalanceRow[] | null = null;
  let balancesError: {
    message?: string;
    details?: string | null;
    hint?: string | null;
    code?: string | null;
  } | null = null;

  const withCompany = await leitura
    .from("v_bank_account_balance")
    .select("bank_account_id,flow_balance,current_balance")
    .eq("company_id", ctx.companyId);

  balances = (withCompany.data ?? []) as BankAccountBalanceRow[];
  balancesError = withCompany.error
    ? {
        message: withCompany.error.message,
        details: withCompany.error.details,
        hint: withCompany.error.hint,
        code: withCompany.error.code,
      }
    : null;

  if (balancesError?.code === "PGRST204") {
    const withoutCompany = await leitura
      .from("v_bank_account_balance")
      .select("bank_account_id,flow_balance,current_balance");
    balances = (withoutCompany.data ?? []) as BankAccountBalanceRow[];
    balancesError = withoutCompany.error
      ? {
          message: withoutCompany.error.message,
          details: withoutCompany.error.details,
          hint: withoutCompany.error.hint,
          code: withoutCompany.error.code,
        }
      : null;
  }

  const balanceMap = new Map(
    (balances ?? []).map((row) => [row.bank_account_id, row as BankAccountBalanceRow])
  );

  const accounts = (baseAccounts ?? [])
    .map((row) => {
      const base = row as BankAccountBaseRow;
      const accountId = typeof base.id === "string" ? base.id : "";
      const balance = accountId ? balanceMap.get(accountId) : undefined;
      return {
        bank_account_id: accountId,
        name: base.name,
        bank_code: base.bank_code,
        bank_name: base.bank_name,
        agency_number: base.agency_number,
        agency_digit: base.agency_digit,
        account_number: base.account_number,
        account_digit: base.account_digit,
        pix_key: base.pix_key,
        logo_key: base.logo_key,
        type: base.type,
        initial_balance: base.initial_balance ?? 0,
        flow_balance: balance?.flow_balance ?? 0,
        current_balance: balance?.current_balance ?? base.initial_balance ?? 0,
        active: base.active ?? true,
      };
    })
    .filter((account) => Boolean(account.bank_account_id));

  const response = {
    ok: true,
    accounts,
    permissions: {
      can_manage_critical: canManageFinancialCritical(ctx, user.id),
      role: ctx.ownerId === user.id ? "owner" : ctx.membership?.role ?? "viewer",
    },
  } as {
    ok: true;
    accounts: typeof accounts;
    permissions: {
      can_manage_critical: boolean;
      role: string;
    };
    balancesWarning?: typeof balancesError;
  };

  if (balancesError) {
    response.balancesWarning = balancesError;
  }

  return NextResponse.json(response);
}

export async function POST(req: Request) {
  const supabase = await createClient();
  const {
    data: { user },
    error: userError,
  } = await supabase.auth.getUser();

  if (userError || !user) {
    return NextResponse.json({ error: "Não autenticado." }, { status: 401 });
  }

  const ctx = await getCompanyContext(supabase, user);
  if (!canManageFinancialCritical(ctx, user.id)) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }

  const {
    data: { session },
    error: sessionError,
  } = await supabase.auth.getSession();
  const accessToken = session?.access_token?.trim();

  if (sessionError || !accessToken) {
    return NextResponse.json({ error: "Sessão inválida para executar esta ação." }, { status: 401 });
  }

  const body = (await req.json().catch(() => null)) as
    | {
        name?: unknown;
        bank_name?: unknown;
        bank_code?: unknown;
        agency_number?: unknown;
        agency_digit?: unknown;
        account_number?: unknown;
        account_digit?: unknown;
        pix_key?: unknown;
        type?: unknown;
        initial_balance?: unknown;
        active?: unknown;
          reason?: unknown;
      }
    | null;

  const name = typeof body?.name === "string" ? body.name.trim() : "";
  const bankName = typeof body?.bank_name === "string" ? body.bank_name.trim() : "";
  const bankCode = typeof body?.bank_code === "string" ? body.bank_code.trim() : "";
  const agencyNumber = typeof body?.agency_number === "string" ? body.agency_number.trim() : "";
  const agencyDigit = typeof body?.agency_digit === "string" ? body.agency_digit.trim() : "";
  const accountNumber = typeof body?.account_number === "string" ? body.account_number.trim() : "";
  const accountDigit = typeof body?.account_digit === "string" ? body.account_digit.trim() : "";
  const pixKey = typeof body?.pix_key === "string" ? body.pix_key.trim() : "";
  const type = normalizeAccountType(body?.type);
  const initialBalance = typeof body?.initial_balance === "number"
    ? body.initial_balance
    : Number(body?.initial_balance);
  const normalizedActive = normalizeActive(body?.active);
  const active = normalizedActive ?? true;
  const reason = typeof body?.reason === "string" ? body.reason.trim() : "";
  const isCashAccount = type === "CAIXA";
  const cashDefaults = getCashAccountDefaults(name);
  const resolvedBankName = isCashAccount ? bankName || cashDefaults.bank_name : bankName;
  const resolvedBankCode = isCashAccount ? bankCode || cashDefaults.bank_code : bankCode;
  const resolvedAgencyNumber = isCashAccount ? agencyNumber || cashDefaults.agency_number : agencyNumber;
  const resolvedAccountNumber = isCashAccount ? accountNumber || cashDefaults.account_number : accountNumber;

  if (!name) {
    return NextResponse.json({ error: "Nome da conta é obrigatório." }, { status: 400 });
  }

  if (!resolvedBankName) {
    return NextResponse.json({ error: "Banco é obrigatório." }, { status: 400 });
  }

  if (!resolvedBankCode) {
    return NextResponse.json({ error: "Código do banco é obrigatório." }, { status: 400 });
  }

  if (!resolvedAgencyNumber) {
    return NextResponse.json({ error: "Agência é obrigatória." }, { status: 400 });
  }

  if (!resolvedAccountNumber) {
    return NextResponse.json({ error: "Conta é obrigatória." }, { status: 400 });
  }

  if (!type) {
    return NextResponse.json({ error: "Tipo de conta inválido." }, { status: 400 });
  }

  if (!Number.isFinite(initialBalance)) {
    return NextResponse.json({ error: "Saldo inicial inválido." }, { status: 400 });
  }

  const { data: created, error: createError } = await supabase
    .from("bank_accounts")
    .insert({
      company_id: ctx.companyId,
      name,
      bank_name: resolvedBankName,
      bank_code: resolvedBankCode,
      agency_number: resolvedAgencyNumber,
      agency_digit: agencyDigit || null,
      account_number: resolvedAccountNumber,
      account_digit: accountDigit || null,
      pix_key: pixKey || null,
      type,
      initial_balance: initialBalance,
      active,
    })
    .select("*")
    .maybeSingle();

  if (createError) {
    console.error({ createError });
    return NextResponse.json(
      {
        error: "Falha ao criar conta bancária.",
        message: createError.message,
        details: createError.details,
        hint: createError.hint,
        code: createError.code,
      },
      { status: 500 }
    );
  }

  const createdRow = created as BankAccountRow | null;
  const createdId =
    typeof createdRow?.id === "string"
      ? createdRow.id
      : typeof createdRow?.bank_account_id === "string"
        ? createdRow.bank_account_id
        : null;

  if (!createdId) {
    return NextResponse.json({ error: "Falha ao criar conta bancária." }, { status: 500 });
  }

  const { data: viewAccount } = await supabase
    .from("v_bank_account_balance")
    .select("bank_account_id,flow_balance,current_balance")
    .eq("bank_account_id", createdId)
    .maybeSingle();

  const createdAccount = created as BankAccountRow | null;
  const account = createdAccount
    ? {
        bank_account_id: createdId,
        name: createdAccount.name ?? null,
        bank_code: createdAccount.bank_code ?? null,
        bank_name: createdAccount.bank_name ?? null,
        agency_number: createdAccount.agency_number ?? null,
        agency_digit: createdAccount.agency_digit ?? null,
        account_number: createdAccount.account_number ?? null,
        account_digit: createdAccount.account_digit ?? null,
        pix_key: createdAccount.pix_key ?? null,
        logo_key: createdAccount.logo_key ?? null,
        type: createdAccount.type ?? null,
        initial_balance: createdAccount.initial_balance ?? 0,
        flow_balance: viewAccount?.flow_balance ?? 0,
        current_balance: viewAccount?.current_balance ?? createdAccount.initial_balance ?? 0,
      }
    : null;

  if (account) {
    const createdSnapshot = await getBankAccountSnapshot(supabase, ctx.companyId, createdId);
    await logBankAccountAuditEvent({
      companyId: ctx.companyId,
      bankAccountId: createdId,
      userId: user.id,
      eventType: "ACCOUNT_CREATED",
      reason: reason || null,
      beforeState: null,
      afterState: createdSnapshot,
      metadata: {
        origin: "api/bank-accounts#create",
      },
    });
  }

  return NextResponse.json({
    ok: true,
    account: account
      ? {
          ...account,
          active: createdAccount?.active ?? active,
        }
      : null,
  });
}

export async function PATCH(req: Request) {
  const supabase = await createClient();
  const {
    data: { user },
    error: userError,
  } = await supabase.auth.getUser();

  if (userError || !user) {
    return NextResponse.json({ error: "Não autenticado." }, { status: 401 });
  }

  const body = (await req.json().catch(() => null)) as
    | {
        id?: unknown;
        patch?: unknown;
      }
    | null;

  const id = typeof body?.id === "string" ? body.id : "";
  if (!id) return NextResponse.json({ error: "Campo 'id' é obrigatório." }, { status: 400 });

  const patch = pickWritablePatch(body?.patch);
  if (!Object.keys(patch).length) {
    return NextResponse.json({ error: "Nada para atualizar." }, { status: 400 });
  }

  if (patch.type !== undefined) {
    const normalized = normalizeAccountType(patch.type);
    if (!normalized) {
      return NextResponse.json({ error: "Tipo de conta inválido." }, { status: 400 });
    }
    patch.type = normalized;
  }

  if (patch.bank_code !== undefined) {
    if (typeof patch.bank_code === "string") {
      const trimmed = patch.bank_code.trim();
      if (!trimmed) {
        return NextResponse.json({ error: "Código do banco é obrigatório." }, { status: 400 });
      }
      patch.bank_code = trimmed;
    }
  }

  if (patch.bank_name !== undefined) {
    if (typeof patch.bank_name === "string") {
      const trimmed = patch.bank_name.trim();
      if (!trimmed) {
        return NextResponse.json({ error: "Banco é obrigatório." }, { status: 400 });
      }
      patch.bank_name = trimmed;
    }
  }

  if (patch.name !== undefined) {
    if (typeof patch.name === "string") {
      const trimmed = patch.name.trim();
      if (!trimmed) {
        return NextResponse.json({ error: "Nome da conta é obrigatório." }, { status: 400 });
      }
      patch.name = trimmed;
    }
  }

  if (patch.agency_number !== undefined) {
    if (typeof patch.agency_number === "string") {
      const trimmed = patch.agency_number.trim();
      if (!trimmed) {
        return NextResponse.json({ error: "Agência é obrigatória." }, { status: 400 });
      }
      patch.agency_number = trimmed;
    }
  }

  if (patch.account_number !== undefined) {
    if (typeof patch.account_number === "string") {
      const trimmed = patch.account_number.trim();
      if (!trimmed) {
        return NextResponse.json({ error: "Conta é obrigatória." }, { status: 400 });
      }
      patch.account_number = trimmed;
    }
  }

  if (patch.agency_digit !== undefined) {
    if (typeof patch.agency_digit === "string") {
      const trimmed = patch.agency_digit.trim();
      patch.agency_digit = trimmed ? trimmed : null;
    }
  }

  if (patch.account_digit !== undefined) {
    if (typeof patch.account_digit === "string") {
      const trimmed = patch.account_digit.trim();
      patch.account_digit = trimmed ? trimmed : null;
    }
  }

  if (patch.pix_key !== undefined) {
    if (typeof patch.pix_key === "string") {
      const trimmed = patch.pix_key.trim();
      patch.pix_key = trimmed ? trimmed : null;
    }
  }

  if (patch.initial_balance !== undefined) {
    return NextResponse.json(
      { error: "O saldo base não pode mais ser editado diretamente. Use o ajuste auditável de saldo." },
      { status: 400 }
    );
  }

  if (patch.active !== undefined) {
    const normalized = normalizeActive(patch.active);
    if (normalized === null) {
      return NextResponse.json({ error: "Status inválido." }, { status: 400 });
    }
    patch.active = normalized;
  }

  const ctx = await getCompanyContext(supabase, user);
  if (!canManageFinancialCritical(ctx, user.id)) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }

  const before = await getBankAccountSnapshot(supabase, ctx.companyId, id);
  if (!before) {
    return NextResponse.json({ error: "Conta bancária não encontrada." }, { status: 404 });
  }

  if (patch.active === false) {
    const dependencies = await getBankAccountDependencies(ctx.companyId, id, ctx.tenantIds?.length ? ctx.tenantIds : [user.id]);
    if (dependencies.open_pdv_cash_sessions > 0) {
      return NextResponse.json(
        { error: "Não é possível desativar a conta enquanto houver caixa PDV aberto nela." },
        { status: 409 }
      );
    }
    if (dependencies.payment_integrations > 0) {
      return NextResponse.json(
        { error: "Desvincule esta conta da integração de pagamento antes de desativá-la." },
        { status: 409 }
      );
    }
  }

  const { data: updated, error: updateError } = await supabase
    .from("bank_accounts")
    .update(patch)
    .eq("id", id)
    .eq("company_id", ctx.companyId)
    .select("id")
    .maybeSingle();

  if (updateError) {
    console.error({ updateError });
    return NextResponse.json(
      {
        error: "Falha ao atualizar conta bancária.",
        message: updateError.message,
        details: updateError.details,
        hint: updateError.hint,
        code: updateError.code,
      },
      { status: 500 }
    );
  }

  if (!updated) {
    return NextResponse.json({ error: "Conta bancária não encontrada." }, { status: 404 });
  }

  const after = await getBankAccountSnapshot(supabase, ctx.companyId, id);
  await logBankAccountAuditEvent({
    companyId: ctx.companyId,
    bankAccountId: id,
    userId: user.id,
    eventType:
      patch.active === false
        ? "ACCOUNT_DEACTIVATED"
        : patch.active === true && before.active === false
          ? "ACCOUNT_REACTIVATED"
          : "ACCOUNT_UPDATED",
    beforeState: before,
    afterState: after,
    metadata: {
      patch,
    },
  });

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

export async function DELETE(req: Request) {
  const supabase = await createClient();
  const {
    data: { user },
    error: userError,
  } = await supabase.auth.getUser();

  if (userError || !user) {
    return NextResponse.json({ error: "Não autenticado." }, { status: 401 });
  }

  const url = new URL(req.url);
  const id = url.searchParams.get("id")?.trim() ?? "";
  const forceMode = url.searchParams.get("force_mode")?.trim() as "migrate" | "exterminate" | null;
  const targetId = url.searchParams.get("target_id")?.trim() ?? null;

  if (!id) {
    return NextResponse.json({ error: "Campo 'id' é obrigatório." }, { status: 400 });
  }

  const ctx = await getCompanyContext(supabase, user);
  if (!canManageFinancialCritical(ctx, user.id)) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }

  const {
    data: { session },
    error: sessionError,
  } = await supabase.auth.getSession();
  const accessToken = session?.access_token?.trim();

  if (sessionError || !accessToken) {
    return NextResponse.json({ error: "Sessão inválida para executar esta ação." }, { status: 401 });
  }

  const before = await getBankAccountSnapshot(supabase, ctx.companyId, id);
  if (!before) {
    return NextResponse.json({ error: "Conta bancária não encontrada." }, { status: 404 });
  }

  const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];
  const dependencies = await getBankAccountDependencies(ctx.companyId, id, tenantIds);

  if (dependencies.total > 0 && !forceMode) {
    return NextResponse.json(
      {
        error: "Esta conta possui histórico ou vínculos operacionais. Você pode inativá-la ou escolher uma ação irreversível (migrar vínculos ou exterminar histórico).",
        dependencies,
      },
      { status: 409 }
    );
  }

  try {
    if (forceMode === "migrate") {
      if (!targetId || targetId === id) {
        return NextResponse.json({ error: "Conta de destino inválida para migração." }, { status: 400 });
      }
      const targetBefore = await getBankAccountSnapshot(supabase, ctx.companyId, targetId);
      if (!targetBefore) {
        return NextResponse.json({ error: "Conta de destino não encontrada." }, { status: 404 });
      }
      await executeHardDeleteOperations(ctx.companyId, id, tenantIds, "migrate", targetId, accessToken);
    } else if (forceMode === "exterminate") {
      await executeHardDeleteOperations(ctx.companyId, id, tenantIds, "exterminate", undefined, accessToken);
    }
  } catch (error: unknown) {
    const message = error instanceof Error ? error.message : "Falha desconhecida ao processar dependências.";
    const stack = error instanceof Error ? error.stack : undefined;
    console.error("HARD DELETE ERROR DETAILED:", message, stack);
    return NextResponse.json(
      {
        error: `Falha ao processar dependências: ${message}`,
        message,
      },
      { status: 500 }
    );
  }

  if (!forceMode) {
    const { error: deleteError } = await supabase
      .from("bank_accounts")
      .delete()
      .eq("id", id)
      .eq("company_id", ctx.companyId);

    if (deleteError) {
      return NextResponse.json(
        {
          error: "Falha ao excluir conta bancária.",
          message: deleteError.message,
          details: deleteError.details,
          hint: deleteError.hint,
          code: deleteError.code,
        },
        { status: 500 }
      );
    }
  }

  await logBankAccountAuditEvent({
    companyId: ctx.companyId,
    bankAccountId: null,
    userId: user.id,
    eventType: "ACCOUNT_DELETED",
    beforeState: before,
    afterState: null,
    metadata: {
      deleted_bank_account_id: id,
      dependencies,
    },
  });

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