/**
 * Mock API layer.
 *
 * Provides filter/aggregate functions for each dashboard view. Every function
 * accepts a DateRange parameter and returns pre-shaped data ready for KPI
 * cards and Recharts components.
 */

import type { DateRange } from '@/lib/utils/date';
import type {
  Work,
  Composer,
  Payee,
  Contract,
  IncomeRecord,
  Transaction,
  Cost,
  Statement,
  SuspenseItem,
  AuditEvent,
} from '@/lib/mock-data/types';
import * as data from '@/lib/mock-data';

// ── Helper utilities ────────────────────────────────────────────────────────

/** Filter an array by a date field within the given range (inclusive). */
export function filterByDateRange<T>(
  items: T[],
  range: DateRange,
  dateField: keyof T,
): T[] {
  return items.filter((item) => {
    const d = item[dateField];
    if (d instanceof Date) {
      return d >= range.start && d <= range.end;
    }
    return true;
  });
}

/** Group items by month (YYYY-MM) and sum a numeric value field. */
export function groupByMonth<T>(
  items: T[],
  dateField: keyof T,
  valueField: keyof T,
): Array<{ month: string; value: number }> {
  const map = new Map<string, number>();
  for (const item of items) {
    const d = item[dateField];
    if (!(d instanceof Date)) continue;
    const month = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
    const val = Number(item[valueField]) || 0;
    map.set(month, (map.get(month) ?? 0) + val);
  }
  return Array.from(map.entries())
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([month, value]) => ({ month, value: Math.round(value * 100) / 100 }));
}

/** Group items by a string field and count occurrences. */
export function groupBy<T>(
  items: T[],
  field: keyof T,
): Array<{ name: string; value: number }> {
  const map = new Map<string, number>();
  for (const item of items) {
    const key = String(item[field]);
    map.set(key, (map.get(key) ?? 0) + 1);
  }
  return Array.from(map.entries())
    .sort((a, b) => b[1] - a[1])
    .map(([name, value]) => ({ name, value }));
}

/** Get the top N items sorted descending by a numeric field. */
export function topN<T>(items: T[], valueField: keyof T, n: number): T[] {
  return [...items]
    .sort((a, b) => (Number(b[valueField]) || 0) - (Number(a[valueField]) || 0))
    .slice(0, n);
}

/** Sum a numeric field across all items. */
export function sumBy<T>(items: T[], field: keyof T): number {
  return Math.round(
    items.reduce((acc, item) => acc + (Number(item[field]) || 0), 0) * 100,
  ) / 100;
}

/** Group items by month and count occurrences (no value summation). */
function countByMonth<T>(
  items: T[],
  dateField: keyof T,
): Array<{ month: string; value: number }> {
  const map = new Map<string, number>();
  for (const item of items) {
    const d = item[dateField];
    if (!(d instanceof Date)) continue;
    const month = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
    map.set(month, (map.get(month) ?? 0) + 1);
  }
  return Array.from(map.entries())
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([month, value]) => ({ month, value }));
}

/** Group items by a string field and sum a numeric value field. */
function groupByAndSum<T>(
  items: T[],
  groupField: keyof T,
  valueField: keyof T,
): Array<{ name: string; value: number }> {
  const map = new Map<string, number>();
  for (const item of items) {
    const key = String(item[groupField]);
    const val = Number(item[valueField]) || 0;
    map.set(key, (map.get(key) ?? 0) + val);
  }
  return Array.from(map.entries())
    .sort((a, b) => b[1] - a[1])
    .map(([name, value]) => ({ name, value: Math.round(value * 100) / 100 }));
}


// ── Dashboard API functions ─────────────────────────────────────────────────

/** Overview Dashboard — KPIs + chart data across all domains. */
export function getOverviewData(range: DateRange) {
  const filteredIncome = filterByDateRange(data.income, range, 'date');
  const filteredContracts = filterByDateRange(data.contracts, range, 'startDate');
  const filteredSuspense = filterByDateRange(data.suspenseItems, range, 'date');
  const filteredStatements = data.statements; // Statements don't have a Date field, use all
  const filteredWorks = filterByDateRange(data.works, range, 'createdAt');
  const filteredComposers = filterByDateRange(data.composers, range, 'createdAt');

  const activeContracts = data.contracts.filter((c) => c.status === 'active');
  const pendingSuspense = filteredSuspense.filter((s) => s.resolutionStatus === 'pending');
  const pendingStatements = filteredStatements.filter((s) => s.status === 'Draft' || s.status === 'Final');

  // Top 10 works by total monthly income within range
  const workIncomeMap = new Map<string, { title: string; totalIncome: number }>();
  for (const w of data.works) {
    const total = w.monthlyIncome.reduce((sum, m) => sum + m.amount, 0);
    workIncomeMap.set(w.id, { title: w.title, totalIncome: total });
  }
  const topWorks = Array.from(workIncomeMap.values())
    .sort((a, b) => b.totalIncome - a.totalIncome)
    .slice(0, 10)
    .map((w) => ({ name: w.title, value: Math.round(w.totalIncome * 100) / 100 }));

  return {
    kpis: {
      totalWorks: data.works.length,
      totalComposers: data.composers.length,
      totalIncome: sumBy(filteredIncome, 'netAmount'),
      activeContracts: activeContracts.length,
      pendingSuspense: pendingSuspense.length,
      pendingStatements: pendingStatements.length,
    },
    incomeOverTime: groupByMonth(filteredIncome, 'date', 'netAmount'),
    incomeByTerritory: groupByAndSum(filteredIncome, 'territory', 'netAmount'),
    topWorks,
    incomeByMediaType: groupByAndSum(filteredIncome, 'mediaType', 'netAmount'),
  };
}

/** Works Dashboard — catalogue composition and performance. */
export function getWorksData(range: DateRange) {
  const filteredWorks = filterByDateRange(data.works, range, 'createdAt');

  // Writer split distribution: bucket by percentage ranges
  const splitBuckets = new Map<string, number>();
  for (const w of filteredWorks) {
    for (const split of w.writerSplits) {
      const bucket =
        split.percentage <= 25 ? '0-25%' :
        split.percentage <= 50 ? '26-50%' :
        split.percentage <= 75 ? '51-75%' : '76-100%';
      splitBuckets.set(bucket, (splitBuckets.get(bucket) ?? 0) + 1);
    }
  }
  const writerSplitDistribution = ['0-25%', '26-50%', '51-75%', '76-100%'].map((name) => ({
    name,
    value: splitBuckets.get(name) ?? 0,
  }));

  // Top 10 works by income
  const worksWithIncome = filteredWorks.map((w) => ({
    name: w.title,
    value: Math.round(w.monthlyIncome.reduce((sum, m) => sum + m.amount, 0) * 100) / 100,
  }));
  const topWorksByIncome = worksWithIncome
    .sort((a, b) => b.value - a.value)
    .slice(0, 10);

  return {
    kpis: {
      totalWorks: filteredWorks.length,
    },
    byGenre: groupBy(filteredWorks, 'genre'),
    byRightsType: groupBy(filteredWorks, 'rightsType'),
    topByIncome: topWorksByIncome,
    addedOverTime: countByMonth(filteredWorks, 'createdAt'),
    writerSplitDistribution,
  };
}

/** Composers Dashboard — income distribution and trends. */
export function getComposersData(range: DateRange) {
  const filteredComposers = filterByDateRange(data.composers, range, 'createdAt');

  // Top 10 by total income
  const composersWithIncome = filteredComposers.map((c) => ({
    ...c,
    totalIncome: c.monthlyIncome.reduce((sum, m) => sum + m.amount, 0),
  }));
  const topByIncome = composersWithIncome
    .sort((a, b) => b.totalIncome - a.totalIncome)
    .slice(0, 10)
    .map((c) => ({ name: c.name, value: Math.round(c.totalIncome * 100) / 100 }));

  // By works count buckets
  const workCountBuckets = new Map<string, number>();
  for (const c of filteredComposers) {
    const bucket =
      c.workCount <= 2 ? '1-2' :
      c.workCount <= 5 ? '3-5' :
      c.workCount <= 10 ? '6-10' : '10+';
    workCountBuckets.set(bucket, (workCountBuckets.get(bucket) ?? 0) + 1);
  }
  const byWorksCount = ['1-2', '3-5', '6-10', '10+'].map((name) => ({
    name,
    value: workCountBuckets.get(name) ?? 0,
  }));

  // Income per top 5 over time
  const top5 = composersWithIncome
    .sort((a, b) => b.totalIncome - a.totalIncome)
    .slice(0, 5);
  const incomePerTop5OverTime = top5.map((c) => ({
    name: c.name,
    data: c.monthlyIncome.map((m) => ({
      month: m.month,
      value: m.amount,
    })),
  }));

  return {
    kpis: {
      totalComposers: filteredComposers.length,
    },
    topByIncome,
    countTrends: countByMonth(filteredComposers, 'createdAt'),
    byWorksCount,
    incomePerTop5OverTime,
  };
}


/** Payees Dashboard — earnings distribution and composition. */
export function getPayeesData(range: DateRange) {
  const allPayees = data.payees;

  // Top 10 by earnings
  const topByEarnings = topN(allPayees, 'cumulativeEarnings', 10).map((p) => ({
    name: p.name,
    value: p.cumulativeEarnings,
  }));

  // Payment over time — use income records as proxy for payee payments
  const filteredIncome = filterByDateRange(data.income, range, 'date');
  const paymentOverTime = groupByMonth(filteredIncome, 'date', 'netAmount');

  // By territory
  const byTerritory = groupBy(allPayees, 'territory');

  return {
    kpis: {
      totalPayees: allPayees.length,
    },
    topByEarnings,
    byType: groupBy(allPayees, 'type'),
    paymentOverTime,
    byTerritory,
  };
}

/** Contracts Dashboard — status, territory, and expiration analysis. */
export function getContractsData(range: DateRange) {
  const filteredContracts = filterByDateRange(data.contracts, range, 'startDate');
  const allContracts = data.contracts;

  const activeCount = allContracts.filter((c) => c.status === 'active').length;
  const inactiveCount = allContracts.filter((c) => c.status === 'inactive').length;

  // Expiring within 90 days from "now" (Dec 31, 2025)
  const now = new Date('2025-12-31');
  const ninetyDaysFromNow = new Date(now.getTime() + 90 * 24 * 60 * 60 * 1000);
  const expiringSoon = allContracts.filter(
    (c) => c.status === 'active' && c.endDate >= now && c.endDate <= ninetyDaysFromNow,
  );

  // Split distribution buckets
  const splitBuckets = new Map<string, number>();
  for (const c of filteredContracts) {
    const bucket =
      c.royaltySplit <= 20 ? '0-20%' :
      c.royaltySplit <= 40 ? '21-40%' :
      c.royaltySplit <= 60 ? '41-60%' :
      c.royaltySplit <= 80 ? '61-80%' : '81-100%';
    splitBuckets.set(bucket, (splitBuckets.get(bucket) ?? 0) + 1);
  }
  const splitDistribution = ['0-20%', '21-40%', '41-60%', '61-80%', '81-100%'].map((name) => ({
    name,
    value: splitBuckets.get(name) ?? 0,
  }));

  return {
    kpis: {
      totalContracts: allContracts.length,
      activeContracts: activeCount,
      expiringIn90Days: expiringSoon.length,
    },
    activeVsInactive: [
      { name: 'Active', value: activeCount },
      { name: 'Inactive', value: inactiveCount },
    ],
    byTerritory: groupBy(filteredContracts, 'territory'),
    expiringSoon: expiringSoon.map((c) => ({
      id: c.id,
      territory: c.territory,
      endDate: c.endDate.toISOString().split('T')[0],
      royaltySplit: c.royaltySplit,
    })),
    splitDistribution,
    byCurrency: groupBy(filteredContracts, 'currency'),
  };
}

/** Income Dashboard — revenue trends, territory, and source analysis. */
export function getIncomeData(range: DateRange) {
  const filtered = filterByDateRange(data.income, range, 'date');

  // Gross vs net by month
  const grossByMonth = groupByMonth(filtered, 'date', 'grossAmount');
  const netByMonth = groupByMonth(filtered, 'date', 'netAmount');
  const grossVsNetByMonth = grossByMonth.map((g) => {
    const net = netByMonth.find((n) => n.month === g.month);
    return {
      month: g.month,
      gross: g.value,
      net: net?.value ?? 0,
    };
  });

  // Top 10 works by income within range
  const workIncomeMap = new Map<string, number>();
  for (const rec of filtered) {
    workIncomeMap.set(rec.workId, (workIncomeMap.get(rec.workId) ?? 0) + rec.netAmount);
  }
  const workLookup = new Map(data.works.map((w) => [w.id, w.title]));
  const topWorks = Array.from(workIncomeMap.entries())
    .sort(([, a], [, b]) => b - a)
    .slice(0, 10)
    .map(([id, value]) => ({
      name: workLookup.get(id) ?? id,
      value: Math.round(value * 100) / 100,
    }));

  return {
    kpis: {
      grossIncome: sumBy(filtered, 'grossAmount'),
      netIncome: sumBy(filtered, 'netAmount'),
      recordCount: filtered.length,
    },
    overTime: groupByMonth(filtered, 'date', 'netAmount'),
    byTerritory: groupByAndSum(filtered, 'territory', 'netAmount'),
    byMediaType: groupByAndSum(filtered, 'mediaType', 'netAmount'),
    bySource: groupByAndSum(filtered, 'source', 'netAmount'),
    grossVsNetByMonth,
    topWorks,
  };
}


/** Transactions Dashboard — volume, types, and financial flow. */
export function getTransactionsData(range: DateRange) {
  const filtered = filterByDateRange(data.transactions, range, 'date');

  return {
    kpis: {
      totalCount: filtered.length,
      totalAmount: sumBy(filtered, 'amount'),
    },
    volumeOverTime: countByMonth(filtered, 'date'),
    byType: groupBy(filtered, 'type'),
    byCurrency: groupByAndSum(filtered, 'currency', 'amount'),
    monthlyTotals: groupByMonth(filtered, 'date', 'amount'),
  };
}

/** Costs Dashboard — cost trends, types, and cost-to-income ratio. */
export function getCostsData(range: DateRange) {
  const filteredCosts = filterByDateRange(data.costs, range, 'date');
  const filteredIncome = filterByDateRange(data.income, range, 'date');

  const totalCosts = sumBy(filteredCosts, 'amount');
  const totalIncome = sumBy(filteredIncome, 'netAmount');
  const costToIncomeRatio = totalIncome > 0 ? totalCosts / totalIncome : 0;

  // Cost-to-income ratio over time
  const costsByMonth = groupByMonth(filteredCosts, 'date', 'amount');
  const incomeByMonth = groupByMonth(filteredIncome, 'date', 'netAmount');
  const ratioOverTime = costsByMonth.map((c) => {
    const inc = incomeByMonth.find((i) => i.month === c.month);
    const incVal = inc?.value ?? 0;
    return {
      month: c.month,
      costs: c.value,
      income: incVal,
      ratio: incVal > 0 ? Math.round((c.value / incVal) * 10000) / 10000 : 0,
    };
  });

  return {
    kpis: {
      totalCosts,
      costToIncomeRatio: Math.round(costToIncomeRatio * 10000) / 10000,
    },
    byType: groupBy(filteredCosts, 'type'),
    trends: groupByMonth(filteredCosts, 'date', 'amount'),
    byCurrency: groupByAndSum(filteredCosts, 'currency', 'amount'),
    ratioOverTime,
  };
}

/** Statements Dashboard — status, amounts, and payee distribution. */
export function getStatementsData(_range: DateRange) {
  // Statements use period strings (YYYY-MM), not Date objects.
  // We use all statements since they don't have a Date field for filtering.
  const allStatements = data.statements;

  const draftCount = allStatements.filter((s) => s.status === 'Draft').length;
  const finalCount = allStatements.filter((s) => s.status === 'Final').length;
  const publishedCount = allStatements.filter((s) => s.status === 'Published').length;
  const totalNetPayable = sumBy(allStatements, 'netPayable');

  // Amounts over time by period
  const amountsByPeriod = new Map<string, number>();
  for (const s of allStatements) {
    amountsByPeriod.set(s.period, (amountsByPeriod.get(s.period) ?? 0) + s.netPayable);
  }
  const amountsOverTime = Array.from(amountsByPeriod.entries())
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([month, value]) => ({ month, value: Math.round(value * 100) / 100 }));

  // Top 10 payees by net payable
  const payeeNetMap = new Map<string, { name: string; value: number }>();
  for (const s of allStatements) {
    const existing = payeeNetMap.get(s.payeeId);
    if (existing) {
      existing.value += s.netPayable;
    } else {
      payeeNetMap.set(s.payeeId, { name: s.payeeName, value: s.netPayable });
    }
  }
  const topPayees = Array.from(payeeNetMap.values())
    .sort((a, b) => b.value - a.value)
    .slice(0, 10)
    .map((p) => ({ name: p.name, value: Math.round(p.value * 100) / 100 }));

  return {
    kpis: {
      totalStatements: allStatements.length,
      draftCount,
      finalCount,
      publishedCount,
      totalNetPayable,
    },
    byStatus: [
      { name: 'Draft', value: draftCount },
      { name: 'Final', value: finalCount },
      { name: 'Published', value: publishedCount },
    ],
    amountsOverTime,
    topPayees,
    byCurrency: groupBy(allStatements, 'currency'),
  };
}

/** Suspense Dashboard — unmatched items, resolution, and sources. */
export function getSuspenseData(range: DateRange) {
  const filtered = filterByDateRange(data.suspenseItems, range, 'date');

  const unmatchedCount = filtered.filter((s) => s.matchType === 'unmatched').length;
  const ambiguousCount = filtered.filter((s) => s.matchType === 'ambiguous').length;
  const resolvedCount = filtered.filter((s) => s.resolutionStatus === 'resolved').length;
  const resolutionRate = filtered.length > 0 ? resolvedCount / filtered.length : 0;

  // Resolution rate over time
  const byMonth = new Map<string, { total: number; resolved: number }>();
  for (const item of filtered) {
    const month = `${item.date.getFullYear()}-${String(item.date.getMonth() + 1).padStart(2, '0')}`;
    const entry = byMonth.get(month) ?? { total: 0, resolved: 0 };
    entry.total++;
    if (item.resolutionStatus === 'resolved') entry.resolved++;
    byMonth.set(month, entry);
  }
  const resolutionRateOverTime = Array.from(byMonth.entries())
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([month, { total, resolved }]) => ({
      month,
      rate: total > 0 ? Math.round((resolved / total) * 10000) / 10000 : 0,
    }));

  return {
    kpis: {
      totalItems: filtered.length,
      unmatchedCount,
      ambiguousCount,
      resolutionRate: Math.round(resolutionRate * 10000) / 10000,
    },
    overTime: countByMonth(filtered, 'date'),
    resolutionRateOverTime,
    byMatchType: [
      { name: 'Unmatched', value: unmatchedCount },
      { name: 'Ambiguous', value: ambiguousCount },
    ],
    bySourceFile: groupBy(filtered, 'sourceFile'),
  };
}

/** Audit Dashboard — activity patterns and user tracking. */
export function getAuditData(range: DateRange) {
  const filtered = filterByDateRange(data.auditEvents, range, 'timestamp');

  const uniqueUsers = new Set(filtered.map((e) => e.userName)).size;

  // Most active users
  const userCounts = new Map<string, number>();
  for (const e of filtered) {
    userCounts.set(e.userName, (userCounts.get(e.userName) ?? 0) + 1);
  }
  const mostActiveUsers = Array.from(userCounts.entries())
    .sort(([, a], [, b]) => b - a)
    .map(([name, value]) => ({ name, value }));

  return {
    kpis: {
      totalEvents: filtered.length,
      uniqueUsers,
    },
    overTime: countByMonth(filtered, 'timestamp'),
    byEntityType: groupBy(filtered, 'entityType'),
    byActionType: groupBy(filtered, 'actionType'),
    mostActiveUsers,
  };
}
