import { NextResponse } from "next/server";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import { createAdminClient, createClient } from "@/lib/supabase-server";
import { criarContaAReceber } from "@/lib/financeiro/contas-a-receber";

type ReceivableStatus = "PENDING" | "PARTIALLY_PAID" | "PAID" | "OVERDUE" | "CANCELED";

function normalizeAmount(value: unknown) {
  const num = Number(value);
  return Number.isFinite(num) ? Math.round(num * 100) / 100 : NaN;
}

function normalizeDate(value: unknown) {
  if (typeof value !== "string") return null;
  const trimmed = value.trim();
  return /^\d{4}-\d{2}-\d{2}$/.test(trimmed) ? trimmed : null;
}

function normalizeStatus(value: unknown): ReceivableStatus | null {
  if (typeof value !== "string") return null;
  const v = value.trim().toUpperCase();
  if (["PENDING", "PARTIALLY_PAID", "PAID", "OVERDUE", "CANCELED"].includes(v)) {
    return v as ReceivableStatus;
  }
  return null;
}

export async function GET(req: Request) {
  const supabase = await createClient();
  const admin = createAdminClient();
  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.receivables.read")) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }

  const url = new URL(req.url);
  const start = normalizeDate(url.searchParams.get("start"));
  const end = normalizeDate(url.searchParams.get("end"));
  const status = normalizeStatus(url.searchParams.get("status"));
  const search = (url.searchParams.get("search") ?? "").trim();

  let query = admin
    .from("accounts_receivable")
    .select(
      "id,description,client_name,client_company,total_amount,paid_amount,due_date,status,created_at,bank_account_id,category_id"
    )
    .eq("company_id", ctx.companyId)
    .order("due_date", { ascending: true });

  /* Um titulo em aberto que venceu antes do periodo continua sendo dinheiro a
     receber, e e justamente o mais urgente da lista. Filtrar so por
     `due_date >= inicio` fazia a tela dizer "nenhuma conta a receber" para quem
     tinha um titulo vencido no mes passado — o pior lugar possivel para
     esconder informacao.

     So vale quando o periodo alcanca hoje: se alguem pediu um mes fechado la
     atras, esta olhando aquele mes e nao quer o resto junto. */
  const ABERTOS = "PENDING,PARTIALLY_PAID,OVERDUE";
  const incluirVencidas = url.searchParams.get("vencidas") === "1";

  if (start && incluirVencidas) {
    query = query.or(
      `due_date.gte.${start},and(due_date.lt.${start},status.in.(${ABERTOS}))`
    );
  } else if (start) {
    query = query.gte("due_date", start);
  }
  if (end) query = query.lte("due_date", end);
  if (status) query = query.eq("status", status);
  if (search) {
    query = query.or(`description.ilike.%${search}%,client_name.ilike.%${search}%,client_company.ilike.%${search}%`);
  }

  const { data, error } = await query;
  if (error) return NextResponse.json({ error: "Falha ao listar contas a receber." }, { status: 500 });

  return NextResponse.json({
    ok: true,
    accounts: (data ?? []).map((item) => ({
      ...item,
      source: "receivable",
      balance: Math.max(0, Number(item.total_amount ?? 0) - Number(item.paid_amount ?? 0)),
    })),
  });
}

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

  const body = (await req.json().catch(() => null)) as Record<string, unknown> | null;
  const resultado = await criarContaAReceber({ admin, companyId: ctx.companyId, userId: user.id, body });
  if (!resultado.ok) return NextResponse.json(resultado.corpo, { status: resultado.status });

  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.receivables.write")) {
    return NextResponse.json({ error: "Sem permissão." }, { status: 403 });
  }
  const admin = createAdminClient();

  const body = (await req.json().catch(() => null)) as Record<string, unknown> | null;
  const id = typeof body?.id === "string" ? body.id : "";
  if (!id) return NextResponse.json({ error: "ID é obrigatório." }, { status: 400 });

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

  const { data: currentAccount, error: currentAccountError } = await admin
    .from("accounts_receivable")
    .select(
      "id,description,client_name,client_company,total_amount,paid_amount,due_date,status,created_at,bank_account_id,category_id"
    )
    .eq("id", id)
    .eq("company_id", ctx.companyId)
    .maybeSingle();

  if (currentAccountError || !currentAccount) {
    return NextResponse.json({ error: "Conta a receber não encontrada." }, { status: 404 });
  }

  if (action === "reverse_receipt") {
    const receiptId = typeof body?.receipt_id === "string" ? body.receipt_id.trim() : "";
    const reason = typeof body?.reason === "string" ? body.reason.trim() : null;
    if (!receiptId) {
      return NextResponse.json({ error: "receipt_id é obrigatório para reabrir o título." }, { status: 400 });
    }

    const { error: reverseError } = await supabase.rpc("reverse_receipt_with_cash_flow", {
      p_receipt_id: receiptId,
      p_reason: reason,
    });

    if (reverseError) {
      const message = reverseError.message ?? "Falha ao reabrir o título.";
      const lowered = message.toLowerCase();
      const status =
        lowered.includes("nao encontrado") || lowered.includes("não encontrado")
          ? 404
          : lowered.includes("ja foi revertido") ||
              lowered.includes("já foi revertido") ||
              lowered.includes("cancelado")
            ? 400
            : 500;
      return NextResponse.json({ error: message }, { status });
    }

    const { data: refreshed } = await admin
      .from("accounts_receivable")
      .select(
        "id,description,client_name,client_company,total_amount,paid_amount,due_date,status,created_at,bank_account_id,category_id"
      )
      .eq("id", id)
      .eq("company_id", ctx.companyId)
      .maybeSingle();

    return NextResponse.json({ ok: true, account: refreshed ?? currentAccount });
  }

  const patch: Record<string, unknown> = {};
  if (typeof body?.description === "string" && body.description.trim()) patch.description = body.description.trim();
  if (body?.total_amount !== undefined) {
    const amount = normalizeAmount(body.total_amount);
    if (!Number.isFinite(amount) || amount <= 0) return NextResponse.json({ error: "Valor inválido." }, { status: 400 });
    if (amount + 0.01 < Number(currentAccount.paid_amount ?? 0)) {
      return NextResponse.json({ error: "Valor total não pode ficar menor que o valor já recebido." }, { status: 400 });
    }
    patch.total_amount = amount;
  }
  if (body?.due_date !== undefined) {
    const dueDate = normalizeDate(body.due_date);
    if (!dueDate) return NextResponse.json({ error: "Vencimento inválido." }, { status: 400 });
    patch.due_date = dueDate;
  }
  if (body?.status !== undefined) {
    const status = normalizeStatus(body.status);
    if (!status) return NextResponse.json({ error: "Status inválido." }, { status: 400 });
    if (status !== "CANCELED") {
      return NextResponse.json(
        { error: "Somente o status CANCELED pode ser definido manualmente. Os demais são calculados automaticamente." },
        { status: 400 }
      );
    }
    if (Number(currentAccount.paid_amount ?? 0) > 0) {
      return NextResponse.json({ error: "Títulos com recebimentos não podem ser cancelados manualmente." }, { status: 400 });
    }
    patch.status = status;
  }
  if (body?.client_name !== undefined) patch.client_name = typeof body.client_name === "string" ? body.client_name.trim() || null : null;
  if (body?.client_company !== undefined) patch.client_company = typeof body.client_company === "string" ? body.client_company.trim() || null : null;
  if (body?.notes !== undefined) patch.notes = typeof body.notes === "string" ? body.notes.trim() || null : null;
  if (body?.bank_account_id !== undefined) patch.bank_account_id = typeof body.bank_account_id === "string" && body.bank_account_id.trim() ? body.bank_account_id.trim() : null;
  if (body?.category_id !== undefined) patch.category_id = typeof body.category_id === "string" && body.category_id.trim() ? body.category_id.trim() : null;

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

  const { data, error } = await admin
    .from("accounts_receivable")
    .update({ ...patch, updated_at: new Date().toISOString() })
    .eq("id", id)
    .eq("company_id", ctx.companyId)
    .select(
      "id,description,client_name,client_company,total_amount,paid_amount,due_date,status,created_at,bank_account_id,category_id"
    )
    .maybeSingle();

  if (error || !data) return NextResponse.json({ error: "Falha ao atualizar conta a receber." }, { status: 500 });

  if (patch.total_amount !== undefined || patch.due_date !== undefined) {
    const { error: syncError } = await supabase.rpc("sync_receivable_summary", { p_receivable_id: id });
    if (syncError) {
      return NextResponse.json({ error: syncError.message || "Falha ao sincronizar o resumo do título." }, { status: 500 });
    }

    const { data: refreshed } = await admin
      .from("accounts_receivable")
      .select(
        "id,description,client_name,client_company,total_amount,paid_amount,due_date,status,created_at,bank_account_id,category_id"
      )
      .eq("id", id)
      .eq("company_id", ctx.companyId)
      .maybeSingle();

    return NextResponse.json({ ok: true, account: refreshed ?? data });
  }

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