/**
 * 年度报告（plan.md「5.2 年度报告」）。
 */

import { addDecimals, compareDecimals, subtractDecimals, negateDecimal } from '../core/decimal';
import type { Transaction } from '../core/types';

export interface AnnualReport {
  year: number;
  totalIncome: string;
  totalExpense: string;
  netIncome: string;
  topCategories: { category: string; amount: string; percentage: number }[];
  monthlyTrend: { month: string; income: string; expense: string }[];
  dayOfWeekBreakdown: { day: string; total: string }[];
  largestTransaction: Transaction | null;
  transactionCount: number;
  averageDailyExpense: string;
}

const DAY_NAMES = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];

export function generateAnnualReport(
  transactions: Transaction[],
  year: number,
): AnnualReport {
  const yearTx = transactions.filter(t => t.date.startsWith(String(year)));

  // 单次遍历：一次性计算所有聚合数据
  const incomeAmounts: string[] = [];
  const expenseAmounts: string[] = [];
  const byCategory = new Map<string, string[]>();
  const monthlyIncome = new Map<string, string[]>();
  const monthlyExpense = new Map<string, string[]>();
  const byDayOfWeek = new Array(7).fill(0).map(() => [] as string[]);
  let largest: Transaction | null = null;
  let maxAmount = '0';
  const uniqueDays = new Set<string>();

  for (const t of yearTx) {
    const dateStr = t.date.slice(0, 10);
    const month = t.date.slice(5, 7);
    uniqueDays.add(dateStr);

    for (const p of t.postings) {
      if (!p.amount) continue;

      // 收入
      if (p.account.startsWith('Income')) {
        const negated = negateDecimal(p.amount);
        incomeAmounts.push(negated);
        if (!monthlyIncome.has(month)) monthlyIncome.set(month, []);
        monthlyIncome.get(month)!.push(negated);
      }

      // 支出
      if (p.account.startsWith('Expenses')) {
        expenseAmounts.push(p.amount);
        const existing = byCategory.get(p.account) ?? [];
        existing.push(p.amount);
        byCategory.set(p.account, existing);
        if (!monthlyExpense.has(month)) monthlyExpense.set(month, []);
        monthlyExpense.get(month)!.push(p.amount);

        // 星期分布
        const dow = new Date(dateStr).getDay();
        byDayOfWeek[dow].push(p.amount);

        // 最大交易（decimal-safe 比较）
        if (compareDecimals(p.amount, maxAmount) > 0) {
          maxAmount = p.amount;
          largest = t;
        }
      }
    }
  }

  const totalIncome = addDecimals(incomeAmounts.length ? incomeAmounts : ['0']);
  const totalExpense = addDecimals(expenseAmounts.length ? expenseAmounts : ['0']);
  const netIncome = subtractDecimals(totalIncome, totalExpense);

  // 月度趋势（单次构建，无循环 filter）
  const monthlyTrend = Array.from({ length: 12 }, (_, i) => {
    const m = String(i + 1).padStart(2, '0');
    return {
      month: m,
      income: addDecimals(monthlyIncome.get(m) ?? []),
      expense: addDecimals(monthlyExpense.get(m) ?? []),
    };
  });

  // Top 分类
  const topCategories = [...byCategory.entries()]
    .map(([category, amounts]) => {
      const amount = addDecimals(amounts);
      return {
        category,
        amount,
        percentage: parseFloat(totalExpense) > 0
          ? Math.round((parseFloat(amount) / parseFloat(totalExpense)) * 10000) / 100
          : 0,
      };
    })
    .sort((a, b) => parseFloat(b.amount) - parseFloat(a.amount))
    .slice(0, 10);

  // 星期分布
  const dayOfWeekBreakdown = byDayOfWeek.map((amounts, i) => ({
    day: DAY_NAMES[i],
    total: addDecimals(amounts),
  }));

  // 日均支出
  const days = uniqueDays.size || 1;
  const averageDailyExpense = (parseFloat(totalExpense) / days).toFixed(2);

  return {
    year,
    totalIncome,
    totalExpense,
    netIncome,
    topCategories,
    monthlyTrend,
    dayOfWeekBreakdown,
    largestTransaction: largest,
    transactionCount: yearTx.length,
    averageDailyExpense,
  };
}
