import type { PartnerBalance } from "./calculations";
import type { ExpenseRecord } from "./expenseFilters";
import { weekKey } from "./expenseFilters";
import type { ProjectDetail } from "./metrics";
import { toNumber } from "./metrics";
import { financialPhaseLabels, operatingCostPhases } from "./finance";

function monthKey(date: Date) {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  return `${year}-${month}`;
}

function dayKey(date: Date) {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  return `${year}-${month}-${day}`;
}

function sortedSeries(map: Map<string, number>, name = "value") {
  return [...map.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([label, value]) => ({ label, [name]: value }));
}

export function expenseByMonth(expenses: ExpenseRecord[]) {
  const map = new Map<string, number>();
  for (const expense of expenses) {
    const key = monthKey(expense.date);
    map.set(key, (map.get(key) ?? 0) + toNumber(expense.finalCost));
  }
  return sortedSeries(map, "expenses");
}

export function expenseByWeek(expenses: ExpenseRecord[]) {
  const map = new Map<string, number>();
  for (const expense of expenses) {
    const key = weekKey(expense.date);
    map.set(key, (map.get(key) ?? 0) + toNumber(expense.finalCost));
  }
  return sortedSeries(map, "expenses");
}

export function expenseByDay(expenses: ExpenseRecord[]) {
  const map = new Map<string, number>();
  for (const expense of expenses) {
    const key = dayKey(expense.date);
    map.set(key, (map.get(key) ?? 0) + toNumber(expense.finalCost));
  }
  return sortedSeries(map, "expenses");
}

export function expenseByCategory(expenses: ExpenseRecord[]) {
  const map = new Map<string, number>();
  for (const expense of expenses) {
    const key = expense.transactionKind === "LABOR_COST" ? "Labor Cost" : expense.category?.name ?? "Uncategorized";
    map.set(key, (map.get(key) ?? 0) + toNumber(expense.finalCost));
  }
  return [...map.entries()]
    .sort((a, b) => b[1] - a[1])
    .map(([label, expenses]) => ({ label, expenses }));
}

export function initialInvestmentByPhase(project: ProjectDetail) {
  const rows = [
    { label: financialPhaseLabels.PRE_PURCHASE, expenses: 0 },
    { label: financialPhaseLabels.ACQUISITION_CLOSING, expenses: 0 },
    { label: financialPhaseLabels.RENOVATION_CAPITAL_IMPROVEMENT, expenses: 0 }
  ];
  for (const expense of project.expenses) {
    if (expense.phase === "PRE_PURCHASE") rows[0].expenses += toNumber(expense.finalCost);
    if (expense.phase === "ACQUISITION_CLOSING" || (expense.phase === "OTHER" && expense.category?.type === "PURCHASE_COST")) {
      rows[1].expenses += toNumber(expense.finalCost);
    }
    if (expense.phase === "RENOVATION_CAPITAL_IMPROVEMENT" || (expense.phase === "OTHER" && expense.category?.type === "RENOVATION_EXPENSE")) {
      rows[2].expenses += toNumber(expense.finalCost);
    }
  }
  return rows;
}

export function operatingExpensesByMonth(project: ProjectDetail) {
  const map = new Map<string, number>();
  for (const expense of project.expenses) {
    if (!operatingCostPhases.includes(expense.phase)) continue;
    const key = monthKey(expense.date);
    map.set(key, (map.get(key) ?? 0) + toNumber(expense.finalCost));
  }
  for (const expense of project.operatingExpenses) {
    const key = monthKey(expense.date);
    map.set(key, (map.get(key) ?? 0) + toNumber(expense.amount));
  }
  return sortedSeries(map, "expenses");
}

export function partnerBalanceChart(balances: PartnerBalance[]) {
  return balances.map((balance) => ({
    label: balance.name,
    paid: balance.totalPaid,
    share: balance.totalResponsibleShare,
    net: balance.netBalance
  }));
}

export function rentByMonth(project: ProjectDetail) {
  const map = new Map<string, { label: string; expected: number; received: number; outstanding: number }>();
  for (const payment of project.rentPayments) {
    const key = monthKey(payment.month);
    const current = map.get(key) ?? { label: key, expected: 0, received: 0, outstanding: 0 };
    current.expected += toNumber(payment.rentExpected);
    current.received += toNumber(payment.rentPaid);
    current.outstanding += Math.max(0, toNumber(payment.balanceDue));
    map.set(key, current);
  }
  return [...map.values()].sort((a, b) => a.label.localeCompare(b.label));
}

export function profitLossByMonth(project: ProjectDetail) {
  const map = new Map<string, { label: string; income: number; expenses: number; net: number }>();
  const ensure = (key: string) => {
    const current = map.get(key) ?? { label: key, income: 0, expenses: 0, net: 0 };
    map.set(key, current);
    return current;
  };

  for (const income of project.incomes) {
    ensure(monthKey(income.date)).income += toNumber(income.amount);
  }
  for (const payment of project.rentPayments) {
    ensure(monthKey(payment.month)).income +=
      toNumber(payment.rentPaid) +
      toNumber(payment.lateFee) +
      toNumber(payment.utilityCharge) +
      toNumber(payment.parkingFee) +
      toNumber(payment.otherCharge);
  }
  for (const deposit of project.securityDeposits) {
    ensure(monthKey(deposit.dateReturned ?? deposit.dateReceived ?? deposit.createdAt)).income += toNumber(deposit.amountWithheld);
  }
  for (const expense of project.operatingExpenses) {
    ensure(monthKey(expense.date)).expenses += toNumber(expense.amount);
  }
  for (const expense of project.expenses) {
    if (operatingCostPhases.includes(expense.phase)) {
      ensure(monthKey(expense.date)).expenses += toNumber(expense.finalCost);
    }
  }

  return [...map.values()]
    .map((item) => ({ ...item, net: item.income - item.expenses }))
    .sort((a, b) => a.label.localeCompare(b.label));
}
