import { NextResponse } from "next/server";
import { canCompany, getCompanyContext } from "@/lib/company-context";
import { createClient } from "@/lib/supabase-server";
import { isCashAccountType } from "@/lib/liquidation-accounts";

export const runtime = "nodejs";

function toPositiveNumber(value: unknown) {
  const parsed = Number(value);
  if (!Number.isFinite(parsed)) return 0;
  return Math.max(0, parsed);
}

function toIsoDate() {
  return new Date().toISOString().slice(0, 10);
}

type SessionRow = {
  id: string;
  company_id: string;
  bank_account_id: string;
  opened_by: string;
  closed_by: string | null;
  status: "open" | "closed";
  opening_float: number | null;
  closing_counted_amount: number | null;
  difference_amount: number | null;
  notes: string | null;
  opened_at: string;
  closed_at: string | null;
  created_at: string;
  updated_at: string;
};

type MovementRow = {
  id: string;
  movement_type: string;
  direction: "IN" | "OUT";
  amount: number;
  description: string | null;
  created_at: string;
};

async function computeSessionTotals(
  supabase: Awaited<ReturnType<typeof createClient>>,
  sessionId: string,
  companyId: string
) {
  const { data: movementsData } = await supabase
    .from("pdv_cash_movements")
    .select("direction,amount,movement_type")
    .eq("company_id", companyId)
    .eq("session_id", sessionId);

  const rows = (movementsData || []) as Array<{ direction: "IN" | "OUT" | null; amount: number | null; movement_type: string | null }>;
  let totalIn = 0;
  let totalOut = 0;
  let cashSales = 0;
  let supplies = 0;
  let withdrawals = 0;
  let change = 0;
  rows.forEach((row) => {
    const amount = Number(row.amount || 0);
    if (amount <= 0) return;
    if (row.direction === "IN") totalIn += amount;
    if (row.direction === "OUT") totalOut += amount;
    if (row.movement_type === "sale_cash") cashSales += amount;
    if (row.movement_type === "supply" || row.movement_type === "opening") supplies += amount;
    if (row.movement_type === "withdrawal") withdrawals += amount;
    if (row.movement_type === "change") change += amount;
  });

  const expectedBalance = totalIn - totalOut;
  return {
    expected_balance: expectedBalance,
    total_in: totalIn,
    total_out: totalOut,
    cash_sales: cashSales,
    supplies,
    withdrawals,
    change,
  };
}

export async function GET(req: Request) {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();
  if (!user) return NextResponse.json({ error: "Não autenticado." }, { status: 401 });

  const ctx = await getCompanyContext(supabase, user);
  if (!canCompany(ctx, user.id, "financial.read")) {
    return NextResponse.json({ error: "Sem permissão para consultar sessão de caixa." }, { status: 403 });
  }

  const url = new URL(req.url);
  const bankAccountId = url.searchParams.get("bank_account_id")?.trim() ?? "";
  const sessionIdParam = url.searchParams.get("session_id")?.trim() ?? "";
  const requestedLimitRaw = Number(url.searchParams.get("movements_limit") ?? "8");
  const requestedLimit = Number.isFinite(requestedLimitRaw) ? Math.max(1, Math.min(200, Math.floor(requestedLimitRaw))) : 8;

  if (sessionIdParam) {
    const { data: requestedSession, error: requestedSessionError } = await supabase
      .from("pdv_cash_sessions")
      .select("*")
      .eq("company_id", ctx.companyId)
      .eq("id", sessionIdParam)
      .maybeSingle();
    if (requestedSessionError) return NextResponse.json({ error: requestedSessionError.message }, { status: 500 });
    if (!requestedSession) return NextResponse.json({ error: "Sessão não encontrada." }, { status: 404 });
    const session = requestedSession as SessionRow;
    const totals = await computeSessionTotals(supabase, session.id, ctx.companyId);
    const { data: movementRows } = await supabase
      .from("pdv_cash_movements")
      .select("id,movement_type,direction,amount,description,created_at")
      .eq("company_id", ctx.companyId)
      .eq("session_id", session.id)
      .order("created_at", { ascending: false })
      .limit(requestedLimit);
    const { data: recentSessionsData } = await supabase
      .from("pdv_cash_sessions")
      .select("*")
      .eq("company_id", ctx.companyId)
      .eq("bank_account_id", session.bank_account_id)
      .order("opened_at", { ascending: false })
      .limit(10);
    return NextResponse.json({
      ok: true,
      session,
      totals,
      movements: (movementRows || []) as MovementRow[],
      recent_sessions: (recentSessionsData || []) as SessionRow[],
    });
  }

  let query = supabase
    .from("pdv_cash_sessions")
    .select("*")
    .eq("company_id", ctx.companyId)
    .eq("status", "open")
    .order("opened_at", { ascending: false })
    .limit(1);
  if (bankAccountId) query = query.eq("bank_account_id", bankAccountId);

  const { data, error } = await query.maybeSingle();
  if (error) return NextResponse.json({ error: error.message }, { status: 500 });
  if (!data) {
    let recentSessions: SessionRow[] = [];
    if (bankAccountId) {
      const { data: recentSessionsData } = await supabase
        .from("pdv_cash_sessions")
        .select("*")
        .eq("company_id", ctx.companyId)
        .eq("bank_account_id", bankAccountId)
        .order("opened_at", { ascending: false })
        .limit(5);
      recentSessions = (recentSessionsData || []) as SessionRow[];
    }
    return NextResponse.json({ ok: true, session: null, recent_sessions: recentSessions, movements: [] });
  }

  const session = data as SessionRow;
  const totals = await computeSessionTotals(supabase, session.id, ctx.companyId);
  const { data: movementRows } = await supabase
    .from("pdv_cash_movements")
    .select("id,movement_type,direction,amount,description,created_at")
    .eq("company_id", ctx.companyId)
    .eq("session_id", session.id)
    .order("created_at", { ascending: false })
    .limit(requestedLimit);
  const { data: recentSessionsData } = await supabase
    .from("pdv_cash_sessions")
    .select("*")
    .eq("company_id", ctx.companyId)
    .eq("bank_account_id", session.bank_account_id)
    .order("opened_at", { ascending: false })
    .limit(5);
  return NextResponse.json({
    ok: true,
    session,
    totals,
    movements: (movementRows || []) as MovementRow[],
    recent_sessions: (recentSessionsData || []) as SessionRow[],
  });
}

export async function POST(req: Request) {
  const supabase = await createClient();
  const {
    data: { user },
  } = await supabase.auth.getUser();
  if (!user) return NextResponse.json({ error: "Não autenticado." }, { status: 401 });

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

  const body = (await req.json().catch(() => null)) as
    | {
        action?: unknown;
        bank_account_id?: unknown;
        session_id?: unknown;
        amount?: unknown;
        counted_amount?: unknown;
        notes?: unknown;
      }
    | null;

  const action = typeof body?.action === "string" ? body.action.trim().toLowerCase() : "";
  const bankAccountId = typeof body?.bank_account_id === "string" ? body.bank_account_id.trim() : "";
  const sessionId = typeof body?.session_id === "string" ? body.session_id.trim() : "";
  const amount = toPositiveNumber(body?.amount);
  const countedAmount = toPositiveNumber(body?.counted_amount);
  const notes = typeof body?.notes === "string" ? body.notes.trim() : "";

  if (action === "open") {
    if (!bankAccountId) return NextResponse.json({ error: "Caixa físico é obrigatório para abrir caixa." }, { status: 400 });
    const { data: bankData, error: bankError } = await supabase
      .from("bank_accounts")
      .select("id,active,type")
      .eq("id", bankAccountId)
      .eq("company_id", ctx.companyId)
      .maybeSingle();
    if (bankError) return NextResponse.json({ error: bankError.message }, { status: 500 });
    if (!bankData) return NextResponse.json({ error: "Caixa físico não encontrado." }, { status: 404 });
    if ((bankData as { active?: boolean | null }).active === false) {
      return NextResponse.json({ error: "Caixa físico inativo." }, { status: 400 });
    }
    if (!isCashAccountType((bankData as { type?: string | null }).type)) {
      return NextResponse.json({ error: "A sessão do PDV só pode ser aberta em uma conta do tipo CAIXA." }, { status: 400 });
    }

    const { data: existingOpen } = await supabase
      .from("pdv_cash_sessions")
      .select("id")
      .eq("company_id", ctx.companyId)
      .eq("bank_account_id", bankAccountId)
      .eq("status", "open")
      .maybeSingle();
    if (existingOpen) {
      return NextResponse.json({ error: "Já existe um caixa físico aberto para esta conta." }, { status: 409 });
    }

    const { data: created, error: createError } = await supabase
      .from("pdv_cash_sessions")
      .insert({
        company_id: ctx.companyId,
        bank_account_id: bankAccountId,
        opened_by: user.id,
        status: "open",
        opening_float: amount,
        notes: notes || null,
      })
      .select("*")
      .single();
    if (createError) return NextResponse.json({ error: createError.message }, { status: 500 });

    const session = created as SessionRow;
    if (amount > 0.0001) {
      const { data: flowData, error: flowError } = await supabase
        .from("cash_flow")
        .insert({
          company_id: ctx.companyId,
          bank_account_id: bankAccountId,
          type: "IN",
          origin: "AJUSTE",
          reference_id: null,
          description: `PDV abertura de caixa sessão ${session.id}`,
          amount,
          date: toIsoDate(),
        })
        .select("id")
        .single();
      if (flowError) return NextResponse.json({ error: flowError.message }, { status: 500 });

      const { error: movementError } = await supabase.from("pdv_cash_movements").insert({
        session_id: session.id,
        company_id: ctx.companyId,
        movement_type: "opening",
        direction: "IN",
        amount,
        description: notes || "Abertura de caixa",
        cash_flow_id: (flowData as { id?: string } | null)?.id || null,
        created_by: user.id,
      });
      if (movementError) return NextResponse.json({ error: movementError.message }, { status: 500 });
    }

    const totals = await computeSessionTotals(supabase, session.id, ctx.companyId);
    return NextResponse.json({ ok: true, session, totals });
  }

  if (!sessionId) return NextResponse.json({ error: "Sessão de caixa é obrigatória." }, { status: 400 });

  const { data: existingSession, error: existingSessionError } = await supabase
    .from("pdv_cash_sessions")
    .select("*")
    .eq("id", sessionId)
    .eq("company_id", ctx.companyId)
    .maybeSingle();
  if (existingSessionError) return NextResponse.json({ error: existingSessionError.message }, { status: 500 });
  if (!existingSession) return NextResponse.json({ error: "Sessão de caixa não encontrada." }, { status: 404 });
  const session = existingSession as SessionRow;
  if (session.status !== "open") return NextResponse.json({ error: "Sessão já está fechada." }, { status: 400 });

  if (action === "supply" || action === "withdrawal") {
    if (amount <= 0.0001) return NextResponse.json({ error: "Informe um valor maior que zero." }, { status: 400 });
    const direction = action === "supply" ? "IN" : "OUT";
    const movementType = action;
    const label = action === "supply" ? "suprimento" : "sangria";
    const { data: flowData, error: flowError } = await supabase
      .from("cash_flow")
      .insert({
        company_id: ctx.companyId,
        bank_account_id: session.bank_account_id,
        type: direction,
        origin: "AJUSTE",
        reference_id: null,
        description: `PDV ${label} sessão ${session.id}`,
        amount,
        date: toIsoDate(),
      })
      .select("id")
      .single();
    if (flowError) return NextResponse.json({ error: flowError.message }, { status: 500 });

    const { error: movementError } = await supabase.from("pdv_cash_movements").insert({
      session_id: session.id,
      company_id: ctx.companyId,
      movement_type: movementType,
      direction,
      amount,
      description: notes || null,
      cash_flow_id: (flowData as { id?: string } | null)?.id || null,
      created_by: user.id,
    });
    if (movementError) return NextResponse.json({ error: movementError.message }, { status: 500 });
    const totals = await computeSessionTotals(supabase, session.id, ctx.companyId);
    return NextResponse.json({ ok: true, session, totals });
  }

  if (action === "close") {
    const totals = await computeSessionTotals(supabase, session.id, ctx.companyId);
    const difference = countedAmount - totals.expected_balance;

    const { data: closedSession, error: closeError } = await supabase
      .from("pdv_cash_sessions")
      .update({
        status: "closed",
        closed_by: user.id,
        closed_at: new Date().toISOString(),
        closing_counted_amount: countedAmount,
        difference_amount: difference,
        notes: notes || session.notes || null,
        updated_at: new Date().toISOString(),
      })
      .eq("id", session.id)
      .eq("company_id", ctx.companyId)
      .eq("status", "open")
      .select("*")
      .single();
    if (closeError) return NextResponse.json({ error: closeError.message }, { status: 500 });

    return NextResponse.json({ ok: true, session: closedSession, totals, difference });
  }

  return NextResponse.json({ error: "Ação inválida." }, { status: 400 });
}
