import { NextResponse } from "next/server";
import { createAdminClient, createClient } from "@/lib/supabase-server";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import {
  baixarContaAPagar,
  criarContaAPagar,
  ensureRecurringInstancesUntilMonth,
  getAccountsPayableByIdWithFallback,
  insertSupplierActivity,
  listAccountsPayableWithFallback,
  normalizeAmount,
  normalizePayableStatus,
  parseMonthRange,
  pickWritablePatch,
  readNullableStringField,
  updateAccountsPayableWithFallback,
} from "@/lib/financeiro/contas-a-pagar";

export async function GET(req: Request) {
  // 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. Validar permissão e obter company_id
  const ctx = await getCompanyContext(supabase, user);
  if (!canCompany(ctx, user.id, "financial.payables.read")) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }
  const admin = createAdminClient();

  const url = new URL(req.url);
  const month = url.searchParams.get("month")?.trim() ?? "";
  const status = url.searchParams.get("status")?.trim() ?? "";
  const bankAccountId = url.searchParams.get("bank_account_id")?.trim() ?? "";
  const categoryId = url.searchParams.get("category_id")?.trim() ?? "";
  const supplierId = url.searchParams.get("supplier_id")?.trim() ?? "";
  const supplierQuery = url.searchParams.get("supplier")?.trim() ?? "";
  const purchaseOrderId = url.searchParams.get("purchase_order_id")?.trim() ?? "";

  const monthRange = month ? parseMonthRange(month) : null;
  if (monthRange) {
    await ensureRecurringInstancesUntilMonth(admin, ctx.companyId, month);
  }
  const { data, error, missingColumns } = await listAccountsPayableWithFallback(admin, {
    companyId: ctx.companyId,
    monthRange,
    bankAccountId,
    categoryId,
    supplierId,
    supplierQuery,
    purchaseOrderId,
  });
  if (error) {
    return NextResponse.json({ error: "Falha ao listar contas a pagar." }, { status: 500 });
  }

  const { data: bankAccounts, error: bankError } = await admin
    .from("bank_accounts")
    .select("id,name,bank_name,active")
    .eq("company_id", ctx.companyId);

  if (bankError) {
    return NextResponse.json({ error: "Falha ao listar contas bancárias." }, { status: 500 });
  }

  const shouldLoadSupplierMap =
    missingColumns.has("supplier_name") ||
    (supplierQuery && !missingColumns.has("supplier_id"));
  const supplierIds = shouldLoadSupplierMap
    ? Array.from(
        new Set(
          (data ?? [])
            .map((account) => (typeof account.supplier_id === "string" ? account.supplier_id : ""))
            .filter(Boolean)
        )
      )
    : [];
  const supplierMap = new Map<string, string>();

  if (supplierIds.length > 0) {
    const { data: suppliers } = await admin
      .from("suppliers")
      .select("id,name")
      .eq("company_id", ctx.companyId)
      .in("id", supplierIds)
      .is("deleted_at", null);

    for (const supplier of suppliers ?? []) {
      if (typeof supplier.id === "string") {
        supplierMap.set(supplier.id, typeof supplier.name === "string" ? supplier.name : "");
      }
    }
  }

  const bankMap = new Map(
    (bankAccounts ?? [])
      .filter((bank) => typeof bank.id === "string")
      .map((bank) => [
        bank.id as string,
        {
          name: bank.name ?? null,
          bank_name: bank.bank_name ?? null,
          active: bank.active ?? true,
        },
      ])
  );

  const today = new Date().toISOString().slice(0, 10);
  const normalizedStatus = normalizePayableStatus(status);

  const accounts = (data ?? []).map((account) => {
    const bankAccountId = readNullableStringField(account.bank_account_id);
    const bank = bankAccountId ? bankMap.get(bankAccountId) ?? null : null;
    const dueDate = typeof account.due_date === "string" ? account.due_date : "";
    const shouldOverdue =
      account.status !== "PAID" &&
      account.status !== "CANCELED" &&
      dueDate &&
      dueDate < today;

    return {
      ...account,
      notes: typeof account.notes === "string" ? account.notes : null,
      is_recurring: account.is_recurring === true,
      recurrence_rule: typeof account.recurrence_rule === "string" ? account.recurrence_rule : null,
      supplier_name:
        typeof account.supplier_name === "string"
          ? account.supplier_name
          : typeof account.supplier_id === "string"
            ? supplierMap.get(account.supplier_id) ?? null
            : null,
      status: shouldOverdue ? "OVERDUE" : account.status,
      bank_account_name: bank?.name ?? null,
      bank_name: bank?.bank_name ?? null,
      bank_active: bank?.active ?? null,
    };
  });

  let filtered = normalizedStatus ? accounts.filter((account) => account.status === normalizedStatus) : accounts;

  if (supplierQuery && missingColumns.has("supplier_name")) {
    const supplierTerm = supplierQuery.toLowerCase();
    filtered = filtered.filter((account) => (account.supplier_name ?? "").toLowerCase().includes(supplierTerm));
  }

  if (purchaseOrderId && missingColumns.has("purchase_order_id")) {
    filtered = [];
  }

  // 4. Retornar dados conforme especificação
  return NextResponse.json({
    ok: true,
    accounts: filtered,
  });
}

export async function POST(req: Request) {
  // 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. Validar permissão e obter company_id
  const ctx = await getCompanyContext(supabase, user);
  if (!canCompany(ctx, user.id, "financial.payables.write")) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }
  const admin = createAdminClient();

  // 3. Validar payload obrigatório
  const body = (await req.json().catch(() => null)) as
    | {
        description?: unknown;
        amount?: unknown;
        due_date?: unknown;
        bank_account_id?: unknown;
        category_id?: unknown;
        supplier_id?: unknown;
        purchase_order_id?: unknown;
        notes?: unknown;
        is_recurring?: unknown;
        recurrence_rule?: unknown;
        payment_method?: unknown;
      }
    | null;

  const resultado = await criarContaAPagar({ supabase, admin, companyId: ctx.companyId, userId: user.id, body });
  if (!resultado.ok) return NextResponse.json(resultado.corpo, { status: resultado.status });

  // 5. Retornar conta criada
  return NextResponse.json({ ok: true, account: resultado.account });
}

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 ctx = await getCompanyContext(supabase, user);
  if (!canCompany(ctx, user.id, "financial.payables.write")) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }
  const admin = createAdminClient();

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

  const action = typeof body?.action === "string" ? body?.action.trim() : "";
  const accountId = typeof body?.id === "string" ? body.id : "";

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

  const { data: existing, error: existingError } = await getAccountsPayableByIdWithFallback(admin, ctx.companyId, accountId);

  if (existingError) {
    return NextResponse.json({ error: "Falha ao buscar conta a pagar." }, { status: 500 });
  }

  if (!existing) {
    return NextResponse.json({ error: "Conta a pagar não encontrada." }, { status: 404 });
  }

  if (action === "pay") {
    const resultado = await baixarContaAPagar({
      supabase,
      admin,
      companyId: ctx.companyId,
      userId: user.id,
      accountId,
      existing,
      payload: body?.payload,
    });
    if (!resultado.ok) return NextResponse.json(resultado.corpo, { status: resultado.status });
    return NextResponse.json({ ok: true, account: resultado.account });
  }

  if (action === "undo_pay") {
    /* Estorna com contra-lancamento em vez de apagar o fluxo de caixa.
       A versao anterior marcava a conta como pendente e so depois tentava o
       delete — que os gatilhos de imutabilidade sempre recusam. O resultado
       era conta pendente com a saida ainda lancada: dinheiro contado como
       gasto para uma conta que voltou a dever.
       A funcao faz as duas coisas na mesma transacao, entao ou tudo acontece
       ou nada acontece. */
    const motivo =
      typeof (body?.payload as Record<string, unknown> | undefined)?.reason === "string"
        ? String((body?.payload as Record<string, unknown>).reason)
        : null;

    const { error: reverseError } = await supabase.rpc("reverse_payable_with_cash_flow", {
      p_payable_id: accountId,
      p_reason: motivo,
    });

    if (reverseError) {
      return NextResponse.json(
        { error: reverseError.message ?? "Falha ao desfazer pagamento." },
        { status: 500 }
      );
    }

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

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

  if (patch.description !== undefined) {
    if (typeof patch.description !== "string" || !patch.description.trim()) {
      return NextResponse.json({ error: "Descrição é obrigatória." }, { status: 400 });
    }
    patch.description = patch.description.trim();
  }

  if (patch.amount !== undefined) {
    const amount = normalizeAmount(patch.amount);
    if (!Number.isFinite(amount) || amount <= 0) {
      return NextResponse.json({ error: "Valor inválido." }, { status: 400 });
    }
    patch.amount = amount;
  }

  if (patch.due_date !== undefined) {
    if (typeof patch.due_date !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(patch.due_date)) {
      return NextResponse.json({ error: "Data de vencimento inválida." }, { status: 400 });
    }
  }

  if (patch.bank_account_id !== undefined) {
    if (typeof patch.bank_account_id !== "string" || !patch.bank_account_id.trim()) {
      return NextResponse.json({ error: "Conta bancária é obrigatória." }, { status: 400 });
    }
    const { data: bankAccount, error: bankAccountError } = await supabase
      .from("bank_accounts")
      .select("id")
      .eq("id", patch.bank_account_id)
      .eq("company_id", ctx.companyId)
      .eq("active", true)
      .maybeSingle();

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

    if (!bankAccount) {
      return NextResponse.json({ error: "Conta bancária inválida." }, { status: 400 });
    }
  }

  if (patch.supplier_id !== undefined) {
    if (patch.supplier_id === null || patch.supplier_id === "") {
      patch.supplier_id = null;
      patch.supplier_name = null;
    } else if (typeof patch.supplier_id === "string") {
      const supplierId = patch.supplier_id.trim();
      const { data: supplier, error: supplierError } = await admin
        .from("suppliers")
        .select("id,name")
        .eq("id", supplierId)
        .eq("company_id", ctx.companyId)
        .is("deleted_at", null)
        .maybeSingle();

      if (supplierError) {
        return NextResponse.json({ error: "Falha ao validar fornecedor." }, { status: 500 });
      }
      if (!supplier?.id) {
        return NextResponse.json({ error: "Fornecedor inválido." }, { status: 400 });
      }
      patch.supplier_id = supplierId;
      patch.supplier_name = typeof supplier.name === "string" ? supplier.name : null;
    }
  }

  if (patch.purchase_order_id !== undefined) {
    if (patch.purchase_order_id === null || patch.purchase_order_id === "") {
      patch.purchase_order_id = null;
    } else if (typeof patch.purchase_order_id === "string") {
      const purchaseOrderId = patch.purchase_order_id.trim();
      const { data: purchaseOrder, error: purchaseOrderError } = await admin
        .from("supplier_purchase_orders")
        .select("id,supplier_id")
        .eq("id", purchaseOrderId)
        .eq("company_id", ctx.companyId)
        .maybeSingle();

      if (purchaseOrderError) {
        return NextResponse.json({ error: "Falha ao validar compra vinculada." }, { status: 500 });
      }

      if (!purchaseOrder?.id) {
        return NextResponse.json({ error: "Compra vinculada inválida." }, { status: 400 });
      }

      const finalSupplierId =
        typeof (patch.supplier_id ?? existing.supplier_id) === "string"
          ? String(patch.supplier_id ?? existing.supplier_id)
          : null;

      if (finalSupplierId && purchaseOrder.supplier_id && purchaseOrder.supplier_id !== finalSupplierId) {
        return NextResponse.json({ error: "A compra vinculada pertence a outro fornecedor." }, { status: 400 });
      }

      patch.purchase_order_id = purchaseOrderId;
    }
  }

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

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

      if (patch.category_id) {
         const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(patch.category_id as string);
         if (!isUuid) {
            const categoryName = patch.category_id as string;
            
            // Buscar categoria existente pelo nome
            const { data: existingCategory } = await admin
              .from("financial_categories")
              .select("id")
              .eq("company_id", ctx.companyId)
              .ilike("name", categoryName)
              .maybeSingle();
              
            if (existingCategory) {
              patch.category_id = existingCategory.id;
            } else {
              // Criar nova categoria
              const { data: newCategory, error: createError } = await admin
                .from("financial_categories")
                .insert({
                  company_id: ctx.companyId,
                  name: categoryName,
                  icon: "tag"
                })
                .select("id")
                .single();
                
              if (!createError && newCategory) {
                patch.category_id = newCategory.id;
              } else {
                 // Fallback or error?
                 console.error("Erro ao criar categoria no PATCH:", createError);
                 return NextResponse.json({ error: "Falha ao processar categoria." }, { status: 500 });
              }
            }
         }
      }
    }
  }

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

  if (patch.notes !== undefined) {
    if (typeof patch.notes === "string") {
      patch.notes = patch.notes.trim() || null;
    } else if (patch.notes !== null) {
      patch.notes = null;
    }
  }

  const { data: updated, error: updateError } = await updateAccountsPayableWithFallback(
    admin,
    ctx.companyId,
    accountId,
    patch
  );

  if (updateError) {
    return NextResponse.json({ error: "Falha ao atualizar conta a pagar." }, { status: 500 });
  }

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

  await insertSupplierActivity(
    admin,
    ctx.companyId,
    typeof ((patch.supplier_id ?? existing.supplier_id) as unknown) === "string"
      ? String(patch.supplier_id ?? existing.supplier_id)
      : null,
    user.id,
    accountId,
    "payable.updated",
    `Conta a pagar atualizada: ${String(updated.description || existing.description || "sem descrição")}.`,
    patch
  );

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

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 body = (await req.json().catch(() => null)) as { id?: unknown } | null;
  const accountId = typeof body?.id === "string" ? body.id : "";
  if (!accountId) return NextResponse.json({ error: "Campo 'id' é obrigatório." }, { status: 400 });

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

  /* Estorna antes de apagar: depois do delete a funcao nao encontraria mais a
     linha para saber valor e conta. O contra-lancamento fica no historico junto
     com o original, e os dois se anulam — em vez do lancamento orfao que a
     versao anterior deixava. */
  const { error: reverseBeforeDeleteError } = await supabase.rpc(
    "reverse_payable_with_cash_flow",
    { p_payable_id: accountId, p_reason: "Conta a pagar removida" }
  );

  if (reverseBeforeDeleteError) {
    return NextResponse.json(
      { error: reverseBeforeDeleteError.message ?? "Falha ao estornar antes de excluir." },
      { status: 500 }
    );
  }

  const { data: deleted, error: deleteError } = await admin
    .from("accounts_payable")
    .delete()
    .eq("id", accountId)
    .eq("company_id", ctx.companyId)
    .select("id,supplier_id")
    .maybeSingle();

  if (deleteError) {
    return NextResponse.json({ error: "Falha ao excluir conta a pagar." }, { status: 500 });
  }

  if (!deleted) {
    return NextResponse.json({ error: "Conta a pagar não encontrada." }, { status: 404 });
  }

  await insertSupplierActivity(
    admin,
    ctx.companyId,
    typeof deleted.supplier_id === "string" ? deleted.supplier_id : null,
    user.id,
    deleted.id,
    "payable.deleted",
    "Conta a pagar removida.",
    {}
  );

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