import type { ImportedEvent, TransactionDraft } from '../core/types';
import { resolveSourceAccount } from '../rules/rules';
import { parseDecimal, formatDecimal } from '../core/decimal';


/**
 * 转账智能识别（参考 AutoAccounting 的 TransferRecognizer）。
 *
 * 核心场景：Income + Expend → Transfer（账户间转账）
 *   例如：支付宝（支出 100）+ 银行卡（收入 100）= 从支付宝转到银行卡
 *
 * 判定条件：
 *   1. 金额相同、币种相同、时间在窗口内
 *   2. ★ 双方账户都是资产负债表科目（Assets:* 或 Liabilities:*）
 *      ——这是区分「转账」与「收支」的关键：Expenses:餐饮 + Assets:微信 不是转账
 *
 * 执行顺序（plan.md「2.7」）：转账识别**先于去重**。
 *   一笔账单既可能是转账的一方又可能是重复时，转账识别优先（合并为单条转账）。
 */

export type TransferConfidence = 'high' | 'medium' | 'low';

export interface TransferPair {
  /** 合并后的转账草稿（已 ready for 入库）。 */
  draft: TransactionDraft;
  /** 构成转账的两个原始事件 id。 */
  leftEventId: string;
  rightEventId: string;
  confidence: TransferConfidence;
  reason: string;
}

export interface RecognizeTransfersResult {
  /** 识别出的转账（已合并为单条草稿）。 */
  transfers: TransferPair[];
  /** 未配对成功的事件（继续走后续去重/分类流程）。 */
  remaining: ImportedEvent[];
}

export interface TransferConfig {
  /** 时间窗口（小时），默认 3 天（72 小时）。 */
  timeWindowHours: number;
}

export const DEFAULT_TRANSFER_CONFIG: TransferConfig = {
  timeWindowHours: 72,
};

/**
 * 判断账户是否为资产负债表科目（Assets 或 Liabilities）。
 * 转账的定义：资金在资产负债表科目之间流动，不涉及损益科目。
 */
function isBalanceSheetAccount(account: string): boolean {
  return account.startsWith('Assets:') || account.startsWith('Liabilities:');
}

/** 归一化金额绝对值字符串，确保不同精度/格式可配对（例如 100 和 100.00）。 */
function normalizeAmount(amt: string): string {
  const abs = amt.startsWith('-') ? amt.slice(1) : amt;
  try {
    const parsed = parseDecimal(abs);
    return formatDecimal(parsed.coefficient, parsed.scale);
  } catch {
    return abs;
  }
}

/**
 * 识别转账配对：Income + Expend → Transfer。
 *
 * @param events 待识别的事件列表
 * @param config 配置
 */
export function recognizeTransfers(
  events: ImportedEvent[],
  config: TransferConfig = DEFAULT_TRANSFER_CONFIG,
): RecognizeTransfersResult {
  const used = new Set<string>();
  const transfers: TransferPair[] = [];
  const windowMs = config.timeWindowHours * 3600 * 1000;

  // 收入事件和支出事件分别收集（转账 = 一收一支配对）
  // direction === 'transfer' 的事件按金额正负归类：负数=支出方，正数=收入方
  // 避免同一笔 transfer 事件同时出现在两个列表中导致错误配对
  const incomes = events.filter(e =>
    (e.direction === 'income' || (e.direction === 'transfer' && !e.amount.startsWith('-'))) && !used.has(e.id)
  );
  const expenses = events.filter(e =>
    (e.direction === 'expense' || (e.direction === 'transfer' && e.amount.startsWith('-'))) && !used.has(e.id)
  );

  // 构建支出事件的金额索引：按 "amount|currency" 分组，加速配对查找
  const expenseByAmount = new Map<string, ImportedEvent[]>();
  for (const expense of expenses) {
    const absAmount = normalizeAmount(expense.amount);
    const key = `${absAmount}|${expense.currency}`;
    const list = expenseByAmount.get(key);
    if (list) list.push(expense);
    else expenseByAmount.set(key, [expense]);
  }

  for (const income of incomes) {
    if (used.has(income.id)) continue;
    const incomeAbs = normalizeAmount(income.amount);
    const key = `${incomeAbs}|${income.currency}`;
    const candidates = expenseByAmount.get(key);
    if (!candidates) continue;
    const match = findPair(income, candidates, used, windowMs);
    if (match) {
      transfers.push(match);
      used.add(match.leftEventId);   // 支出方
      used.add(match.rightEventId);  // 收入方
    }
  }

  const remaining = events.filter(e => !used.has(e.id));
  return { transfers, remaining };
}

function findPair(
  income: ImportedEvent,
  candidates: ImportedEvent[],
  used: Set<string>,
  windowMs: number,
): TransferPair | null {
  const incomeTime = Date.parse(income.occurredAt);
  // 防护：Date.parse 返回 NaN 时跳过（避免无效时间格式绕过窗口检查）
  if (isNaN(incomeTime)) return null;
  
  const incomeAbs = normalizeAmount(income.amount);

  for (const expense of candidates) {
    if (used.has(expense.id)) continue;

    // 时间窗口
    const expenseTime = Date.parse(expense.occurredAt);
    // 防护：Date.parse 返回 NaN 时跳过
    if (isNaN(expenseTime)) continue;
    if (Math.abs(incomeTime - expenseTime) > windowMs) continue;

    // 置信度评估
    const confidence = assessConfidence(income, expense);

    // 构造转账草稿：从支出方账户 → 收入方账户
    const fromAccount = resolveSourceAccount(expense);
    const toAccount = resolveSourceAccount(income);

    // ★ 核心校验：双方必须都是资产负债表科目，且不能是同一明确账户（避免已知账户自转账）
    if (!isBalanceSheetAccount(fromAccount) || !isBalanceSheetAccount(toAccount)) continue;
    if (fromAccount !== 'Assets:Unknown' && fromAccount === toAccount) continue;

    const amount = incomeAbs;

    const draft: TransactionDraft = {
      date: (income.occurredAt < expense.occurredAt ? income.occurredAt : expense.occurredAt).slice(0, 10),
      narration: `转账：${fromAccount} → ${toAccount}`,
      sourceEventIds: [expense.id, income.id],
      postings: [
        { account: fromAccount, amount: `-${amount}`, currency: income.currency },
        { account: toAccount, amount, currency: income.currency },
      ],
    };

    return {
      draft,
      leftEventId: expense.id,
      rightEventId: income.id,
      confidence: confidence.level,
      reason: confidence.reason,
    };
  }

  return null;
}

/** 评估转账置信度。 */
function assessConfidence(
  income: ImportedEvent,
  expense: ImportedEvent,
): { level: TransferConfidence; reason: string } {
  // 不同对手方匹配 → 高/中置信度
  if (income.counterparty && expense.counterparty) {
    if (income.counterparty === expense.counterparty) {
      return { level: 'high', reason: '对手方完全一致' };
    }
    if (income.counterparty.includes(expense.counterparty) || expense.counterparty.includes(income.counterparty)) {
      return { level: 'medium', reason: '对手方匹配' };
    }
  }

  // 仅金额时间匹配 → 低置信度
  return { level: 'low', reason: '金额时间匹配' };
}
