import type { ProjectDetail } from "./metrics";
import { toNumber } from "./metrics";

export type ExpenseRecord = ProjectDetail["expenses"][number];

export type ExpenseFilterParams = {
  projectId?: string;
  partner?: string;
  paidBy?: string;
  category?: string;
  vendor?: string;
  from?: string;
  to?: string;
  day?: string;
  week?: string;
  month?: string;
  year?: string;
  splitMethod?: string;
  phase?: string;
  recordType?: string;
  transactionKind?: string;
  workerId?: string;
  laborPaymentBasis?: string;
  laborPaymentStatus?: string;
  search?: string;
  quick?: string;
};

export function firstParam(value: string | string[] | undefined) {
  return Array.isArray(value) ? value[0] : value;
}

export function parseExpenseFilters(params: Record<string, string | string[] | undefined>): ExpenseFilterParams {
  return {
    projectId: firstParam(params.projectId) ?? "",
    partner: firstParam(params.partner) ?? "",
    paidBy: firstParam(params.paidBy) ?? "",
    category: firstParam(params.category) ?? "",
    vendor: firstParam(params.vendor) ?? "",
    from: firstParam(params.from) ?? "",
    to: firstParam(params.to) ?? "",
    day: firstParam(params.day) ?? "",
    week: firstParam(params.week) ?? "",
    month: firstParam(params.month) ?? "",
    year: firstParam(params.year) ?? "",
    splitMethod: firstParam(params.splitMethod) ?? "",
    phase: firstParam(params.phase) ?? "",
    recordType: firstParam(params.recordType) ?? "",
    transactionKind: firstParam(params.transactionKind) ?? "",
    workerId: firstParam(params.workerId) ?? "",
    laborPaymentBasis: firstParam(params.laborPaymentBasis) ?? "",
    laborPaymentStatus: firstParam(params.laborPaymentStatus) ?? "",
    search: firstParam(params.search) ?? "",
    quick: firstParam(params.quick) ?? ""
  };
}

function dateKey(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 mondayOf(date: Date) {
  const day = date.getDay() || 7;
  const result = new Date(date);
  result.setHours(0, 0, 0, 0);
  result.setDate(result.getDate() - day + 1);
  return result;
}

function addDays(date: Date, days: number) {
  const result = new Date(date);
  result.setDate(result.getDate() + days);
  return result;
}

export function weekKey(date: Date) {
  const target = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
  const dayNum = target.getUTCDay() || 7;
  target.setUTCDate(target.getUTCDate() + 4 - dayNum);
  const yearStart = new Date(Date.UTC(target.getUTCFullYear(), 0, 1));
  const weekNo = Math.ceil(((target.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
  return `${target.getUTCFullYear()}-W${String(weekNo).padStart(2, "0")}`;
}

export function quickRange(quick?: string) {
  if (!quick || quick === "all") return {};
  const now = new Date();
  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  if (quick === "today") return { from: dateKey(today), to: dateKey(today) };
  if (quick === "this-week") {
    const from = mondayOf(today);
    return { from: dateKey(from), to: dateKey(addDays(from, 6)) };
  }
  if (quick === "last-week") {
    const from = addDays(mondayOf(today), -7);
    return { from: dateKey(from), to: dateKey(addDays(from, 6)) };
  }
  if (quick === "this-month") {
    const from = new Date(today.getFullYear(), today.getMonth(), 1);
    const to = new Date(today.getFullYear(), today.getMonth() + 1, 0);
    return { from: dateKey(from), to: dateKey(to) };
  }
  if (quick === "last-month") {
    const from = new Date(today.getFullYear(), today.getMonth() - 1, 1);
    const to = new Date(today.getFullYear(), today.getMonth(), 0);
    return { from: dateKey(from), to: dateKey(to) };
  }
  if (quick === "this-year") {
    return {
      from: `${today.getFullYear()}-01-01`,
      to: `${today.getFullYear()}-12-31`
    };
  }
  return {};
}

export function normalizedExpenseFilters(filters: ExpenseFilterParams): ExpenseFilterParams {
  const quick = quickRange(filters.quick);
  return {
    ...filters,
    from: filters.from || quick.from || "",
    to: filters.to || quick.to || ""
  };
}

export function filterExpenses(expenses: ExpenseRecord[], rawFilters: ExpenseFilterParams) {
  const filters = normalizedExpenseFilters(rawFilters);
  const search = filters.search?.toLowerCase().trim() ?? "";
  const vendor = filters.vendor?.toLowerCase().trim() ?? "";

  return expenses.filter((expense) => {
    const expenseDate = dateKey(expense.date);
    const matchesPartner =
      !filters.partner ||
      expense.paidById === filters.partner ||
      expense.shares.some((share) => share.projectPartnerId === filters.partner);
    const matchesPaidBy = !filters.paidBy || expense.paidById === filters.paidBy;
    const matchesCategory = !filters.category || expense.categoryId === filters.category;
    const matchesVendor = !vendor || (expense.vendor ?? "").toLowerCase().includes(vendor);
    const matchesFrom = !filters.from || expenseDate >= filters.from;
    const matchesTo = !filters.to || expenseDate <= filters.to;
    const matchesDay = !filters.day || expenseDate === filters.day;
    const matchesWeek = !filters.week || weekKey(expense.date) === filters.week;
    const matchesMonth = !filters.month || expenseDate.slice(0, 7) === filters.month;
    const matchesYear = !filters.year || expenseDate.slice(0, 4) === filters.year;
    const matchesSplit = !filters.splitMethod || expense.splitMethod === filters.splitMethod;
    const phaseFilters = filters.phase ? filters.phase.split(",").filter(Boolean) : [];
    const matchesPhase = phaseFilters.length === 0 || phaseFilters.includes(expense.phase);
    const matchesRecordType = !filters.recordType || expense.recordType === filters.recordType;
    const matchesTransactionKind = !filters.transactionKind || expense.transactionKind === filters.transactionKind;
    const matchesWorker = !filters.workerId || expense.workerId === filters.workerId;
    const matchesLaborBasis = !filters.laborPaymentBasis || expense.laborPaymentBasis === filters.laborPaymentBasis;
    const matchesLaborStatus = !filters.laborPaymentStatus || expense.laborPaymentStatus === filters.laborPaymentStatus;
    const haystack = [expense.vendor, expense.description, expense.notes, expense.category?.name, expense.worker?.name, expense.workDescription, expense.workArea]
      .filter(Boolean)
      .join(" ")
      .toLowerCase();
    const matchesSearch = !search || haystack.includes(search);

    return (
      matchesPartner &&
      matchesPaidBy &&
      matchesCategory &&
      matchesVendor &&
      matchesFrom &&
      matchesTo &&
      matchesDay &&
      matchesWeek &&
      matchesMonth &&
      matchesYear &&
      matchesSplit &&
      matchesPhase &&
      matchesRecordType &&
      matchesTransactionKind &&
      matchesWorker &&
      matchesLaborBasis &&
      matchesLaborStatus &&
      matchesSearch
    );
  });
}

export function summarizeExpenses(expenses: ExpenseRecord[]) {
  const totalAmount = expenses.reduce((sum, expense) => sum + toNumber(expense.amount), 0);
  const totalDiscount = expenses.reduce((sum, expense) => sum + toNumber(expense.discount), 0);
  const totalFinalCost = expenses.reduce((sum, expense) => sum + toNumber(expense.finalCost), 0);
  const categoryTotals = new Map<string, number>();
  const paidTotals = new Map<string, number>();

  for (const expense of expenses) {
    const categoryName = expense.transactionKind === "LABOR_COST" ? "Labor Cost" : expense.category?.name ?? "Uncategorized";
    categoryTotals.set(
      categoryName,
      (categoryTotals.get(categoryName) ?? 0) + toNumber(expense.finalCost)
    );
    paidTotals.set(
      expense.paidBy?.partner.name ?? "Unassigned",
      (paidTotals.get(expense.paidBy?.partner.name ?? "Unassigned") ?? 0) + toNumber(expense.finalCost)
    );
  }

  const topCategory = [...categoryTotals.entries()].sort((a, b) => b[1] - a[1])[0];
  const highestPaidPartner = [...paidTotals.entries()].sort((a, b) => b[1] - a[1])[0];

  return {
    totalAmount,
    totalDiscount,
    totalFinalCost,
    count: expenses.length,
    topCategory: topCategory ? { name: topCategory[0], amount: topCategory[1] } : null,
    highestPaidPartner: highestPaidPartner ? { name: highestPaidPartner[0], amount: highestPaidPartner[1] } : null
  };
}

export function expenseExportParams(projectId: string, filters: ExpenseFilterParams) {
  return new URLSearchParams({
    projectId,
    ...(filters.partner ? { partner: filters.partner } : {}),
    ...(filters.paidBy ? { paidBy: filters.paidBy } : {}),
    ...(filters.category ? { category: filters.category } : {}),
    ...(filters.vendor ? { vendor: filters.vendor } : {}),
    ...(filters.from ? { from: filters.from } : {}),
    ...(filters.to ? { to: filters.to } : {}),
    ...(filters.day ? { day: filters.day } : {}),
    ...(filters.week ? { week: filters.week } : {}),
    ...(filters.month ? { month: filters.month } : {}),
    ...(filters.year ? { year: filters.year } : {}),
    ...(filters.splitMethod ? { splitMethod: filters.splitMethod } : {}),
    ...(filters.phase ? { phase: filters.phase } : {}),
    ...(filters.recordType ? { recordType: filters.recordType } : {}),
    ...(filters.transactionKind ? { transactionKind: filters.transactionKind } : {}),
    ...(filters.workerId ? { workerId: filters.workerId } : {}),
    ...(filters.laborPaymentBasis ? { laborPaymentBasis: filters.laborPaymentBasis } : {}),
    ...(filters.laborPaymentStatus ? { laborPaymentStatus: filters.laborPaymentStatus } : {}),
    ...(filters.search ? { search: filters.search } : {}),
    ...(filters.quick ? { quick: filters.quick } : {})
  });
}
