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

const MANUAL_PREFIX = "[MANUAL] ";
const RECEIVABLE_REVERSAL_PREFIX = "[RECEBER:REVERSAO] ";

function parseMonthRange(value: string) {
  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 start = new Date(Date.UTC(year, month - 1, 1, 0, 0, 0));
  const end = new Date(Date.UTC(year, month, 1, 0, 0, 0));
  return {
    start: start.toISOString().slice(0, 10),
    end: end.toISOString().slice(0, 10),
  };
}

function normalizePaymentMethod(value: unknown) {
  if (typeof value !== "string") return null;
  return value.trim().toLowerCase() || null;
}

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

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

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

function normalizeOrigin(
  origin: unknown,
  description: unknown,
  options?: { isReceivableReversal?: boolean; isPaymentReversal?: boolean }
) {
  if (origin === "MANUAL") return "MANUAL";
  if (origin === "AJUSTE" && isManualTagged(description)) return "MANUAL";
  if (origin === "AJUSTE" && (options?.isReceivableReversal || isReceivableReversal(description))) return "RECEBER";
  if (origin === "AJUSTE" && options?.isPaymentReversal) return "PEDIDO";
  return typeof origin === "string" ? origin : "UNKNOWN";
}

function cleanManualDescription(description: unknown) {
  if (typeof description !== "string") return "";
  if (isReceivableReversal(description)) return description.slice(RECEIVABLE_REVERSAL_PREFIX.length).trim();
  return isManualTagged(description) ? description.slice(MANUAL_PREFIX.length).trim() : description;
}

type CashFlowBaseRow = {
  date: string | null;
  description: string | null;
  type: "IN" | "OUT" | null;
  origin: string | null;
  amount: number | null;
  bank_account_id: string | null;
  reference_id: string | null;
  reversal_of_id?: string | null;
  reversal_reason?: string | null;
};

async function loadCashFlowRows(params: {
  supabase: Awaited<ReturnType<typeof createClient>>;
  companyId: string;
  monthRange: { start: string; end: string } | null;
  bankAccountId: string;
}) {
  const buildQuery = (includeReversalFields: boolean) => {
    let query = params.supabase
      .from("cash_flow")
      .select(
        includeReversalFields
          ? "date,description,type,origin,amount,bank_account_id,reference_id,reversal_of_id,reversal_reason"
          : "date,description,type,origin,amount,bank_account_id,reference_id"
      )
      .eq("company_id", params.companyId);

    if (params.monthRange) {
      query = query.gte("date", params.monthRange.start).lt("date", params.monthRange.end);
    }

    if (params.bankAccountId) {
      query = query.eq("bank_account_id", params.bankAccountId);
    }

    return query.order("date", { ascending: false });
  };

  const withReversalFields = await buildQuery(true);
  if (!withReversalFields.error) {
    return {
      data: ((withReversalFields.data ?? []) as unknown as CashFlowBaseRow[]),
      compatibilityMode: false,
    };
  }

  const missingReversalColumn =
    withReversalFields.error.code === "42703" &&
    /reversal_of_id|reversal_reason/i.test(withReversalFields.error.message || "");

  if (!missingReversalColumn) {
    throw withReversalFields.error;
  }

  const withoutReversalFields = await buildQuery(false);
  if (withoutReversalFields.error) {
    throw withoutReversalFields.error;
  }

  return {
    data: (((withoutReversalFields.data ?? []) as unknown as CashFlowBaseRow[])).map((entry) => ({
      ...entry,
      reversal_of_id: null,
      reversal_reason: null,
    })),
    compatibilityMode: true,
  };
}

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.cashflow.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 bankAccountId = url.searchParams.get("bank_account_id")?.trim() ?? "";

  const monthRange = month ? parseMonthRange(month) : null;
  let rawEntries: CashFlowBaseRow[] = [];
  let compatibilityMode = false;
  try {
    const result = await loadCashFlowRows({
      supabase: leitura,
      companyId: ctx.companyId,
      monthRange,
      bankAccountId,
    });
    rawEntries = result.data;
    compatibilityMode = result.compatibilityMode;
  } catch (error) {
    const normalizedError = error as {
      message?: string | null;
      details?: string | null;
      hint?: string | null;
      code?: string | null;
    };
    return NextResponse.json(
      {
        error: "Falha ao listar fluxo de caixa.",
        message: normalizedError.message,
        details: normalizedError.details,
        hint: normalizedError.hint,
        code: normalizedError.code,
      },
      { status: 500 }
    );
  }
  const paymentReferenceIds = Array.from(
    new Set(
      rawEntries
        .filter((entry) => (entry.origin === "PEDIDO" || entry.origin === "AJUSTE") && entry.reference_id)
        .map((entry) => entry.reference_id as string)
    )
  );
  const payableIds = Array.from(
    new Set(
      rawEntries
        .filter((entry) => entry.origin === "PAGAR" && entry.reference_id)
        .map((entry) => entry.reference_id as string)
    )
  );
  const receivableReferenceIds = Array.from(
    new Set(
      rawEntries
        .filter(
          (entry) =>
            (entry.origin === "RECEBER" || entry.origin === "AJUSTE") &&
            entry.reference_id
        )
        .map((entry) => entry.reference_id as string)
    )
  );
  const tenantIds = ctx.tenantIds?.length ? ctx.tenantIds : [user.id];
  const paymentsById = new Map<
    string,
    {
      amount: number | null;
      payment_method: string | null;
      payment_date: string | null;
      bank_account_id: string | null;
      order_id: string | null;
    }
  >();
  const payableDetails = new Map<string, { method: string | null; category: string | null }>();
  const receivableDetails = new Map<string, { method: string | null; category: string | null }>();
  type PayableDetailRow = {
    id: string | null;
    payment_method: string | null;
    financial_categories: { name: string | null }[] | null;
  };

  if (paymentReferenceIds.length > 0) {
    const paymentsQuery = supabase
      .from("payments")
      .select("id,order_id,amount,payment_method,payment_date,bank_account_id,user_id")
      .in("id", paymentReferenceIds)
      .in("user_id", tenantIds);

    const { data: payments } = await paymentsQuery;
    (payments ?? []).forEach((payment) => {
      const paymentId = payment.id as string | null;
      if (!paymentId) return;
      paymentsById.set(paymentId, {
        amount: payment.amount ?? null,
        payment_method: payment.payment_method ?? null,
        payment_date: payment.payment_date ?? null,
        bank_account_id: payment.bank_account_id ?? null,
        order_id: payment.order_id ?? null,
      });
    });
  }

  const canceledOrderIds = new Set<string>();
  const orderIdsFromPayments = Array.from(
    new Set(
      Array.from(paymentsById.values())
        .map((payment) => payment.order_id)
        .filter((value): value is string => typeof value === "string" && value.length > 0)
    )
  );
  if (orderIdsFromPayments.length > 0) {
    const { data: canceledOrders } = await leitura
      .from("orders")
      .select("id")
      .in("id", orderIdsFromPayments)
      .in("user_id", tenantIds)
      .eq("status", "canceled");
    (canceledOrders ?? []).forEach((row) => {
      const id = (row as { id?: string | null }).id;
      if (typeof id === "string") canceledOrderIds.add(id);
    });
  }

  if (payableIds.length > 0) {
    const admin = createAdminClient();
    const { data: payables } = await admin
      .from("accounts_payable")
      .select("id,payment_method,financial_categories(name)")
      .eq("company_id", ctx.companyId)
      .in("id", payableIds);

    const rows = (payables ?? []) as PayableDetailRow[];
    rows.forEach((it) => {
      if (!it?.id) return;
      const categoryName = it.financial_categories?.[0]?.name ?? null;
      payableDetails.set(it.id, {
        method: it.payment_method ?? null,
        category: categoryName,
      });
    });
  }

  if (receivableReferenceIds.length > 0) {
    const { data: receipts } = await leitura
      .from("receivable_receipts")
      .select("id,payment_method,accounts_receivable(category_id,financial_categories(name))")
      .in("id", receivableReferenceIds)
      .eq("company_id", ctx.companyId);

    (receipts ?? []).forEach((item) => {
      const row = item as {
        id?: string | null;
        payment_method?: string | null;
        accounts_receivable?:
          | { category_id?: string | null; financial_categories?: { name?: string | null }[] | { name?: string | null } | null }
          | { category_id?: string | null; financial_categories?: { name?: string | null }[] | { name?: string | null } | null }[]
          | null;
      };
      if (!row?.id) return;
      const account = Array.isArray(row.accounts_receivable) ? row.accounts_receivable[0] : row.accounts_receivable;
      const category = Array.isArray(account?.financial_categories)
        ? account?.financial_categories[0]?.name ?? null
        : account?.financial_categories?.name ?? null;
      receivableDetails.set(row.id, {
        method: row.payment_method ?? null,
        category,
      });
    });
  }

  const entries = rawEntries
    .filter((entry) => {
      if (entry.origin !== "PEDIDO") return true;
      const paymentMatch = entry.reference_id ? paymentsById.get(entry.reference_id) ?? null : null;
      const orderId = paymentMatch?.order_id ?? null;
      if (!orderId) return true;
      return !canceledOrderIds.has(orderId);
    })
    .map((entry) => {
    const paymentMatch = entry.reference_id ? paymentsById.get(entry.reference_id) ?? null : null;
    const payableDetail = entry.origin === "PAGAR" && entry.reference_id
      ? payableDetails.get(entry.reference_id as string)
      : null;
    const receivableDetail = entry.reference_id
      ? receivableDetails.get(entry.reference_id as string) ?? null
      : null;
    const normalizedOrigin = normalizeOrigin(entry.origin, entry.description, {
      isReceivableReversal: entry.origin === "AJUSTE" && Boolean(receivableDetail) && Boolean(entry.reversal_of_id),
      isPaymentReversal: entry.origin === "AJUSTE" && Boolean(paymentMatch) && Boolean(entry.reversal_of_id),
    });
    let categoryName: string | null = null;
    if (normalizedOrigin === "PEDIDO") categoryName = "Receita";
    if (normalizedOrigin === "RECEBER") categoryName = "Recebíveis";
    if (normalizedOrigin === "AJUSTE") categoryName = "Ajuste";
    if (normalizedOrigin === "MANUAL") categoryName = "Lançamento manual";
    if (entry.origin === "MANUAL") categoryName = "Lançamento manual";

    if (entry.origin === "PAGAR" && payableDetail?.category) {
      categoryName = payableDetail.category;
    }
    if (normalizedOrigin === "RECEBER" && receivableDetail?.category) {
      categoryName = receivableDetail.category;
    }

    return {
      date: entry.date,
      description: cleanManualDescription(entry.description),
      type: entry.type,
      origin: normalizedOrigin,
      amount: entry.amount,
      bank_account_id: entry.bank_account_id,
      reference_id: entry.reference_id ?? null,
      category_name: categoryName,
      payment_method: normalizePaymentMethod(paymentMatch?.payment_method ?? payableDetail?.method ?? receivableDetail?.method),
      order_id: paymentMatch?.order_id ?? null,
    };
    });

  return NextResponse.json({
    ok: true,
    entries,
    compatibility_mode: compatibilityMode,
  });
}

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

  const body = (await req.json().catch(() => null)) as Record<string, unknown> | null;
  const type = body?.type === "OUT" ? "OUT" : body?.type === "IN" ? "IN" : null;
  const amount = Number(body?.amount);
  const description = typeof body?.description === "string" ? body.description.trim() : "";
  const bankAccountId = typeof body?.bank_account_id === "string" ? body.bank_account_id.trim() : "";
  const date = normalizeIsoDate(body?.date);
  const categoryName = typeof body?.category_name === "string" ? body.category_name.trim() : "";

  if (!type) return NextResponse.json({ error: "Tipo inválido." }, { status: 400 });
  if (!Number.isFinite(amount) || amount <= 0) {
    return NextResponse.json({ error: "Valor inválido." }, { status: 400 });
  }
  if (!description) return NextResponse.json({ error: "Descrição obrigatória." }, { status: 400 });
  if (!bankAccountId) return NextResponse.json({ error: "Conta bancária obrigatória." }, { status: 400 });
  if (!date) return NextResponse.json({ error: "Data inválida." }, { status: 400 });

  const fullDescription = categoryName ? `${description} • ${categoryName}` : description;
  const taggedDescription = `${MANUAL_PREFIX}${fullDescription}`;

  const { error } = await supabase.from("cash_flow").insert({
    company_id: ctx.companyId,
    bank_account_id: bankAccountId,
    type,
    origin: "AJUSTE",
    reference_id: null,
    description: taggedDescription,
    amount,
    date,
  });

  if (error) {
    return NextResponse.json({ error: "Falha ao criar lançamento manual.", message: error.message }, { status: 500 });
  }

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