import { getAuthHeaders } from "./api-client";
import {
  AccountPayable,
  AccountReceivable,
  CashMovement,
  CreateFinancialEntryDTO,
  FinanceFilterParams,
  FinanceStats,
} from "@/types/finance";

const API_BASE = (process.env.NEXT_PUBLIC_API_URL || "http://localhost:3333").replace(/\/$/, "") + "/api";

const INITIAL_RECEIVABLES: AccountReceivable[] = [];

const INITIAL_PAYABLES: AccountPayable[] = [];

const INITIAL_CASH_MOVEMENTS: CashMovement[] = [];

let localReceivables: AccountReceivable[] = [...INITIAL_RECEIVABLES];
let localPayables: AccountPayable[] = [...INITIAL_PAYABLES];
let localCashMovements: CashMovement[] = [...INITIAL_CASH_MOVEMENTS];

export async function getReceivables(params?: FinanceFilterParams): Promise<{
  items: AccountReceivable[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
}> {
  try {
    const queryParams = new URLSearchParams();
    if (params?.search) queryParams.set("search", params.search);
    if (params?.status && params.status !== "ALL") queryParams.set("status", params.status);
    if (params?.page) queryParams.set("page", String(params.page));
    if (params?.limit) queryParams.set("limit", String(params.limit));

    const res = await fetch(`${API_BASE}/finance/receivables?${queryParams.toString()}`, {
      headers: getAuthHeaders(),
      cache: "no-store",
    });

    if (res.ok) {
      const data = await res.json();
      if (data && Array.isArray(data.items)) {
        return data;
      }
      if (Array.isArray(data)) {
        return { items: data, total: data.length, page: 1, limit: 50, totalPages: 1 };
      }
    }
  } catch {
    // fallback
  }

  let filtered = [...localReceivables];

  if (params?.search) {
    const term = params.search.toLowerCase();
    filtered = filtered.filter(
      (r) =>
        r.description.toLowerCase().includes(term) ||
        (r.customer && r.customer.name.toLowerCase().includes(term))
    );
  }

  if (params?.status && params.status !== "ALL") {
    filtered = filtered.filter((r) => r.status === params.status);
  }

  const page = params?.page || 1;
  const limit = params?.limit || 20;
  const total = filtered.length;
  const totalPages = Math.ceil(total / limit) || 1;
  const startIndex = (page - 1) * limit;
  const items = filtered.slice(startIndex, startIndex + limit);

  return { items, total, page, limit, totalPages };
}

export async function getPayables(params?: FinanceFilterParams): Promise<{
  items: AccountPayable[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
}> {
  try {
    const queryParams = new URLSearchParams();
    if (params?.search) queryParams.set("search", params.search);
    if (params?.status && params.status !== "ALL") queryParams.set("status", params.status);
    if (params?.page) queryParams.set("page", String(params.page));
    if (params?.limit) queryParams.set("limit", String(params.limit));

    const res = await fetch(`${API_BASE}/finance/payables?${queryParams.toString()}`, {
      headers: getAuthHeaders(),
      cache: "no-store",
    });

    if (res.ok) {
      const data = await res.json();
      if (data && Array.isArray(data.items)) {
        return data;
      }
      if (Array.isArray(data)) {
        return { items: data, total: data.length, page: 1, limit: 50, totalPages: 1 };
      }
    }
  } catch {
    // fallback
  }

  let filtered = [...localPayables];

  if (params?.search) {
    const term = params.search.toLowerCase();
    filtered = filtered.filter((p) => p.description.toLowerCase().includes(term));
  }

  if (params?.status && params.status !== "ALL") {
    filtered = filtered.filter((p) => p.status === params.status);
  }

  const page = params?.page || 1;
  const limit = params?.limit || 20;
  const total = filtered.length;
  const totalPages = Math.ceil(total / limit) || 1;
  const startIndex = (page - 1) * limit;
  const items = filtered.slice(startIndex, startIndex + limit);

  return { items, total, page, limit, totalPages };
}

export async function getCashFlow(params?: FinanceFilterParams): Promise<{
  items: CashMovement[];
  total: number;
  page: number;
  limit: number;
  totalPages: number;
}> {
  try {
    const queryParams = new URLSearchParams();
    if (params?.search) queryParams.set("search", params.search);
    if (params?.page) queryParams.set("page", String(params.page));
    if (params?.limit) queryParams.set("limit", String(params.limit));

    const res = await fetch(`${API_BASE}/finance/cash-flow?${queryParams.toString()}`, {
      headers: getAuthHeaders(),
      cache: "no-store",
    });

    if (res.ok) {
      const data = await res.json();
      if (data && Array.isArray(data.items)) {
        return data;
      }
      if (Array.isArray(data)) {
        return { items: data, total: data.length, page: 1, limit: 50, totalPages: 1 };
      }
    }
  } catch {
    // fallback
  }

  let filtered = [...localCashMovements];

  if (params?.search) {
    const term = params.search.toLowerCase();
    filtered = filtered.filter(
      (c) =>
        c.description.toLowerCase().includes(term) ||
        (c.category && c.category.toLowerCase().includes(term))
    );
  }

  const page = params?.page || 1;
  const limit = params?.limit || 10;
  const total = filtered.length;
  const totalPages = Math.ceil(total / limit) || 1;
  const startIndex = (page - 1) * limit;
  const items = filtered.slice(startIndex, startIndex + limit);

  return { items, total, page, limit, totalPages };
}

export async function getFinanceStats(): Promise<FinanceStats> {
  try {
    const res = await fetch(`${API_BASE}/finance/stats`, {
      headers: getAuthHeaders(),
      cache: "no-store",
    });

    if (res.ok) {
      const stats = await res.json();
      if (stats && typeof stats.totalReceivable === "number") return stats;
    }
  } catch {
    // fallback
  }

  const totalReceivable = localReceivables
    .filter((r) => r.status === "OPEN" || r.status === "OVERDUE")
    .reduce((acc, r) => acc + (Number(r.amount) || 0), 0);

  const totalPayable = localPayables
    .filter((p) => p.status === "OPEN" || p.status === "OVERDUE")
    .reduce((acc, p) => acc + (Number(p.amount) || 0), 0);

  const overdueAmount = localReceivables
    .filter((r) => r.status === "OVERDUE")
    .reduce((acc, r) => acc + (Number(r.amount) || 0), 0);

  const netProjected = totalReceivable - totalPayable;

  return { totalReceivable, totalPayable, netProjected, overdueAmount };
}

export async function createFinancialEntry(data: CreateFinancialEntryDTO) {
  try {
    const endpoint = data.entryType === "RECEIVABLE" ? "receivables" : "payables";
    const res = await fetch(`${API_BASE}/finance/${endpoint}`, {
      method: "POST",
      headers: getAuthHeaders(),
      body: JSON.stringify({
        ...(data.entryType === "RECEIVABLE" ? { customerId: data.entityId } : { supplierId: data.entityId }),
        description: data.description,
        amount: data.amount,
        dueDate: data.dueDate,
      }),
    });

    if (res.ok) {
      return await res.json();
    }
  } catch {
    // fallback
  }

  if (data.entryType === "RECEIVABLE") {
    const newRec: AccountReceivable = {
      id: `rec-${Date.now()}`,
      companyId: "comp-default",
      customerId: data.entityId,
      description: data.description,
      amount: Number(data.amount) || 0,
      dueDate: data.dueDate,
      status: "OPEN",
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
      customer: { id: data.entityId, name: "Cliente Lançamento", document: "00.000.000/0001-00" },
    };
    localReceivables = [newRec, ...localReceivables];
    return newRec;
  } else {
    const newPay: AccountPayable = {
      id: `pay-${Date.now()}`,
      companyId: "comp-default",
      supplierId: data.entityId,
      description: data.description,
      amount: Number(data.amount) || 0,
      dueDate: data.dueDate,
      status: "OPEN",
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString(),
      supplier: { id: data.entityId, name: "Fornecedor Lançamento" },
    };
    localPayables = [newPay, ...localPayables];
    return newPay;
  }
}

export async function payReceivable(id: string, body?: { amountPaid?: number; paidAt?: string; newDueDate?: string }) {
  try {
    const res = await fetch(`${API_BASE}/finance/receivables/${id}/pay`, {
      method: "PATCH",
      headers: getAuthHeaders(),
      body: body ? JSON.stringify(body) : undefined,
    });

    if (res.ok) {
      return await res.json();
    }
  } catch {
    // fallback
  }

  const idx = localReceivables.findIndex((r) => r.id === id);
  if (idx !== -1) {
    const amountPaid = body?.amountPaid ? Number(body.amountPaid) : Number(localReceivables[idx].amount);
    const paidAt = body?.paidAt ? new Date(body.paidAt).toISOString() : new Date().toISOString();
    const isPartial = amountPaid < Number(localReceivables[idx].amount);

    if (isPartial) {
      const newDueDateStr = body?.newDueDate
        ? new Date(body.newDueDate).toISOString()
        : localReceivables[idx].dueDate;
      const now = new Date();
      now.setHours(0, 0, 0, 0);
      const dueDateOnly = new Date(newDueDateStr);
      dueDateOnly.setHours(0, 0, 0, 0);
      const newStatus = dueDateOnly >= now ? "OPEN" : "OVERDUE";
      const desc = localReceivables[idx].description.replace(/ \(Saldo Parcial\)$/, "");

      // Update original record with remaining balance
      localReceivables[idx] = {
        ...localReceivables[idx],
        amount: Number(localReceivables[idx].amount) - amountPaid,
        dueDate: newDueDateStr,
        status: newStatus as any,
        description: `${desc} (Saldo Parcial)`,
      };

      // Add a new PAID record for the amount paid
      localReceivables.push({
        ...localReceivables[idx],
        id: `rec-partial-${Date.now()}`,
        description: `${desc} (Baixa Parcial)`,
        amount: amountPaid,
        dueDate: paidAt,
        status: "PAID" as any,
        paidAt,
      });

      localCashMovements = [
        {
          id: `cm-${Date.now()}`,
          companyId: "comp-default",
          type: "INCOME",
          description: `Recebimento Parcial: ${desc}`,
          amount: amountPaid,
          occurredAt: paidAt,
          category: "Locação",
          createdAt: new Date().toISOString(),
        },
        ...localCashMovements,
      ];
    } else {
      localReceivables[idx] = {
        ...localReceivables[idx],
        status: "PAID",
        paidAt,
      };
      localCashMovements = [
        {
          id: `cm-${Date.now()}`,
          companyId: "comp-default",
          type: "INCOME",
          description: `Recebimento: ${localReceivables[idx].description}`,
          amount: localReceivables[idx].amount,
          occurredAt: paidAt,
          category: "Locação",
          createdAt: new Date().toISOString(),
        },
        ...localCashMovements,
      ];
    }
  }
}

export async function payPayable(id: string, body?: { amountPaid?: number; paidAt?: string; newDueDate?: string }) {
  try {
    const res = await fetch(`${API_BASE}/finance/payables/${id}/pay`, {
      method: "PATCH",
      headers: getAuthHeaders(),
      body: body ? JSON.stringify(body) : undefined,
    });

    if (res.ok) {
      return await res.json();
    }
  } catch {
    // fallback
  }

  const idx = localPayables.findIndex((p) => p.id === id);
  if (idx !== -1) {
    const amountPaid = body?.amountPaid ? Number(body.amountPaid) : Number(localPayables[idx].amount);
    const paidAt = body?.paidAt ? new Date(body.paidAt).toISOString() : new Date().toISOString();
    const isPartial = amountPaid < Number(localPayables[idx].amount);

    if (isPartial) {
      const newDueDateStr = body?.newDueDate
        ? new Date(body.newDueDate).toISOString()
        : localPayables[idx].dueDate;
      const now = new Date();
      now.setHours(0, 0, 0, 0);
      const dueDateOnly = new Date(newDueDateStr);
      dueDateOnly.setHours(0, 0, 0, 0);
      const newStatus = dueDateOnly >= now ? "OPEN" : "OVERDUE";
      const desc = localPayables[idx].description.replace(/ \(Saldo Parcial\)$/, "");

      // Update original record with remaining balance
      localPayables[idx] = {
        ...localPayables[idx],
        amount: Number(localPayables[idx].amount) - amountPaid,
        dueDate: newDueDateStr,
        status: newStatus as any,
        description: `${desc} (Saldo Parcial)`,
      };

      // Add a new PAID record for the amount paid
      localPayables.push({
        ...localPayables[idx],
        id: `pay-partial-${Date.now()}`,
        description: `${desc} (Baixa Parcial)`,
        amount: amountPaid,
        dueDate: paidAt,
        status: "PAID" as any,
        paidAt,
      });

      localCashMovements = [
        {
          id: `cm-${Date.now()}`,
          companyId: "comp-default",
          type: "EXPENSE",
          description: `Pagamento Parcial: ${desc}`,
          amount: amountPaid,
          occurredAt: paidAt,
          category: "Operacional",
          createdAt: new Date().toISOString(),
        },
        ...localCashMovements,
      ];
    } else {
      localPayables[idx] = {
        ...localPayables[idx],
        status: "PAID",
        paidAt,
      };
      localCashMovements = [
        {
          id: `cm-${Date.now()}`,
          companyId: "comp-default",
          type: "EXPENSE",
          description: `Pagamento: ${localPayables[idx].description}`,
          amount: localPayables[idx].amount,
          occurredAt: paidAt,
          category: "Operacional",
          createdAt: new Date().toISOString(),
        },
        ...localCashMovements,
      ];
    }
  }
}
