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

type PeriodRange = {
  start: string;
  end: string;
  endExclusive: string;
  label: string;
};

type CashFlowRow = {
  date: string | null;
  type: string | null;
  amount: number | null;
  origin: string | null;
  reference_id?: string | null;
  description?: string | null;
};

const MANUAL_PREFIX = "[MANUAL] ";

function isManualTagged(description: unknown) {
  return typeof description === "string" && description.startsWith(MANUAL_PREFIX);
}

function normalizeOrigin(origin: unknown, description: unknown) {
  if (origin === "MANUAL") return "MANUAL";
  if (origin === "AJUSTE" && isManualTagged(description)) return "MANUAL";
  return typeof origin === "string" ? origin : "UNKNOWN";
}

function parseMonthRange(value: string): PeriodRange | null {
  if (!/^\d{4}-\d{2}$/.test(value)) return null;
  const [yearRaw, monthRaw] = value.split("-");
  const year = Number(yearRaw);
  const month = Number(monthRaw);
  if (!Number.isInteger(year) || !Number.isInteger(month)) return null;
  if (month < 1 || month > 12) return null;
  const startDate = new Date(Date.UTC(year, month - 1, 1, 0, 0, 0));
  const endExclusiveDate = new Date(Date.UTC(year, month, 1, 0, 0, 0));
  const endDate = new Date(Date.UTC(year, month, 0, 0, 0, 0));
  const start = startDate.toISOString().slice(0, 10);
  const endExclusive = endExclusiveDate.toISOString().slice(0, 10);
  const end = endDate.toISOString().slice(0, 10);
  const labelBase = new Intl.DateTimeFormat("pt-BR", {
    month: "long",
    year: "numeric",
    timeZone: "UTC",
  }).format(startDate);
  const label = labelBase.charAt(0).toUpperCase() + labelBase.slice(1);
  return { start, end, endExclusive, label };
}

function parseDateParam(value: string) {
  if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) return null;
  const date = new Date(`${value}T00:00:00.000Z`);
  if (Number.isNaN(date.getTime())) return null;
  return value;
}

function normalizeType(value: unknown): "IN" | "OUT" | null {
  if (typeof value !== "string") return null;
  const normalized = value.trim().toLowerCase();
  if (normalized === "in" || normalized === "entrada") return "IN";
  if (normalized === "out" || normalized === "saida" || normalized === "saída") return "OUT";
  return null;
}

function isRevenueEntry(origin: string, type: "IN" | "OUT") {
  return (origin === "PEDIDO" || origin === "RECEBER" || origin === "AJUSTE" || origin === "MANUAL") && type === "IN";
}

function isExpenseEntry(origin: string, type: "IN" | "OUT") {
  return (origin === "PAGAR" || origin === "AJUSTE" || origin === "MANUAL") && type === "OUT";
}

export async function GET(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.dre.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 url = new URL(req.url);
  const month = url.searchParams.get("month")?.trim() ?? "";
  const startParam = url.searchParams.get("start")?.trim() ?? "";
  const endParam = url.searchParams.get("end")?.trim() ?? "";
  const bankAccountId = url.searchParams.get("bank_account_id")?.trim() ?? "";

  let period: PeriodRange | null = null;
  let useEndExclusive = true;

  if (month) {
    period = parseMonthRange(month);
  } else if (startParam && endParam) {
    const start = parseDateParam(startParam);
    const end = parseDateParam(endParam);
    if (start && end) {
      const startDate = new Date(`${start}T00:00:00.000Z`);
      const endDate = new Date(`${end}T00:00:00.000Z`);
      if (startDate.getTime() <= endDate.getTime()) {
        period = {
          start,
          end,
          endExclusive: end,
          label: `${start} a ${end}`,
        };
        useEndExclusive = false;
      }
    }
  }

  if (!period) {
    return NextResponse.json(
      { error: "Informe 'month' (YYYY-MM) ou 'start'/'end' (YYYY-MM-DD)." },
      { status: 400 }
    );
  }

  let query = supabase
    .from("cash_flow")
    .select("date,type,amount,origin,reference_id,description")
    .eq("company_id", ctx.companyId)
    .gte("date", period.start);

  query = useEndExclusive ? query.lt("date", period.endExclusive) : query.lte("date", period.end);

  if (bankAccountId && bankAccountId !== "all") {
    query = query.eq("bank_account_id", bankAccountId);
  }

  const { data, error } = await query;
  if (error) {
    console.error("Erro ao carregar DRE:", error);
    return NextResponse.json(
      {
        error: "Falha ao carregar DRE.",
        message: error.message,
        details: error.details,
        hint: error.hint,
        code: error.code,
      },
      { status: 500 }
    );
  }

  let rows = (data ?? []) as CashFlowRow[];
  const pedidoReferenceIds = Array.from(
    new Set(
      rows
        .filter((row) => row.origin === "PEDIDO")
        .map((row) => row.reference_id)
        .filter((value): value is string => typeof value === "string" && value.length > 0)
    )
  );

  if (pedidoReferenceIds.length > 0) {
    const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];
    const { data: paymentRows } = await leitura
      .from("payments")
      .select("id,order_id")
      .in("id", pedidoReferenceIds)
      .in("user_id", tenantIds);
    const paymentToOrder = new Map<string, string>();
    const orderIds = new Set<string>();
    (paymentRows ?? []).forEach((payment) => {
      const id = (payment as { id?: string | null }).id;
      const orderId = (payment as { order_id?: string | null }).order_id;
      if (typeof id === "string" && typeof orderId === "string") {
        paymentToOrder.set(id, orderId);
        orderIds.add(orderId);
      }
    });

    // Some legacy rows may store order_id directly in reference_id.
    pedidoReferenceIds.forEach((ref) => {
      if (!paymentToOrder.has(ref)) orderIds.add(ref);
    });

    if (orderIds.size > 0) {
      const { data: canceledOrders } = await leitura
        .from("orders")
        .select("id")
        .in("id", Array.from(orderIds))
        .in("user_id", tenantIds)
        .eq("status", "canceled");
      const canceledOrderSet = new Set((canceledOrders ?? []).map((row) => (row as { id: string }).id));
      rows = rows.filter((row) => {
        if (row.origin !== "PEDIDO") return true;
        const referenceId = row.reference_id;
        if (!referenceId) return true;
        const linkedOrderId = paymentToOrder.get(referenceId) || referenceId;
        return !canceledOrderSet.has(linkedOrderId);
      });
    }
  }
  const revenueMap = new Map<string, number>();
  const expensesMap = new Map<string, number>();
  const dailyMap = new Map<string, { in: number; out: number }>();
  let revenue = 0;
  let expenses = 0;

  rows.forEach((row) => {
    const normalized = normalizeType(row.type);
    if (!normalized) return;
    const amount = Number(row.amount ?? 0) || 0;
    const origin = typeof row.origin === "string" && row.origin.trim() ? row.origin.trim() : "UNKNOWN";
    const normalizedOrigin = normalizeOrigin(origin, row.description);
    const date = typeof row.date === "string" && row.date ? row.date : null;
    if (isRevenueEntry(normalizedOrigin, normalized)) {
      revenue += amount;
      revenueMap.set(normalizedOrigin, (revenueMap.get(normalizedOrigin) ?? 0) + amount);
    }
    if (isExpenseEntry(normalizedOrigin, normalized)) {
      expenses += amount;
      expensesMap.set(normalizedOrigin, (expensesMap.get(normalizedOrigin) ?? 0) + amount);
    }
    if (date) {
      const current = dailyMap.get(date) ?? { in: 0, out: 0 };
      if (isRevenueEntry(normalizedOrigin, normalized)) {
        current.in += amount;
      }
      if (isExpenseEntry(normalizedOrigin, normalized)) {
        current.out += amount;
      }
      dailyMap.set(date, current);
    }
  });

  const breakdownRevenue = Array.from(revenueMap.entries())
    .map(([label, amount]) => ({ label, amount }))
    .sort((a, b) => b.amount - a.amount);

  const breakdownExpenses = Array.from(expensesMap.entries())
    .map(([label, amount]) => ({ label, amount }))
    .sort((a, b) => b.amount - a.amount);

  const daily = Array.from(dailyMap.entries())
    .map(([date, values]) => ({
      date,
      in: values.in,
      out: values.out,
      net: values.in - values.out,
    }))
    .sort((a, b) => a.date.localeCompare(b.date));

  return NextResponse.json({
    ok: true,
    period: {
      start: period.start,
      end: period.end,
      label: period.label,
    },
    kpis: {
      revenue,
      expenses,
      net: revenue - expenses,
    },
    breakdown: {
      revenue: breakdownRevenue,
      expenses: breakdownExpenses,
    },
    series: {
      daily,
    },
  });
}
