import { classifyWithCategories, inferPostingAmounts } from '../rules/rules';
import { batchDedup, dedupAgainstHistory, type DedupConfig, type DuplicateDetail } from '../transaction/dedup';
import { recognizeTransfers, type TransferConfig, type TransferPair } from '../transaction/transferRecognizer';
import { compareDecimals } from '../core/decimal';
import type { Category } from '../taxonomy/categories';
import type { ImportedEvent, LedgerIndex, Rule, Transaction, TransactionDraft } from '../core/types';
import { logger } from '../../utils/logger';

/**
 * 账单处理责任链（参考 AutoAccounting 的 BillService.analyze + deduplicationMutex）。
 *
 * 统一所有入口（手动/CSV/OCR/通知/短信/截图）的处理顺序与并发控制。
 * 设计见 plan.md「2.8 账单处理责任链」与「决策 6 跨进程通信」。
 *
 * 执行顺序（必须严格遵循）：
 *   1. 转账识别（先于去重，合并 Income+Expend → Transfer）
 *   2. 多通道去重（时间窗口 + 金额）
 *   3. 与历史交易去重（避免重复入账）
 *   4. 资产映射 + 分类（规则 → 分类关键词 → Uncategorized）
 *   5. 输出草稿（入库由调用方走 LedgerStore）
 */

export interface PipelineContext {
  ledger: LedgerIndex;
  rules: Rule[];
  categories: Category[];
  /** 已入账的历史交易（用于跨会话去重）。 */
  history: Transaction[];
  dedupConfig?: DedupConfig;
  transferConfig?: TransferConfig;
}

export interface ProcessedDraft {
  draft: TransactionDraft;
  /** 来源事件 id（用于去重标记）。 */
  sourceEventIds: string[];
  /** 是否为转账合并。 */
  isTransfer: boolean;
  /** 分类降级原因（若有）。 */
  categoryFallbackReason?: string;
}

export interface PipelineResult {
  /** 待入库的草稿。 */
  drafts: ProcessedDraft[];
  /** 被去重丢弃的事件。 */
  duplicates: ImportedEvent[];
  /** 详细的去重匹配信息。 */
  duplicateDetails?: DuplicateDetail[];
  /** 转账识别统计。 */
  transferCount: number;
}

/**
 * 检查转账草稿是否已存在于历史交易中（避免重复入账）。
 * 匹配条件：日期相同 + 金额相同 + 两个 posting 账户相同。
 */
function isTransferInHistory(transfer: TransferPair, history: Transaction[]): boolean {
  const draft = transfer.draft;
  const draftDate = draft.date;
  const draftFromAccount = draft.postings[0]?.account;
  const draftToAccount = draft.postings[1]?.account;

  if (!draftFromAccount || !draftToAccount) return false;

  return history.some(t => {
    if (t.date !== draftDate) return false;
    // 检查是否包含转账双方的账户
    const pFrom = t.postings.find(p => p.account === draftFromAccount);
    const pTo = t.postings.find(p => p.account === draftToAccount);
    if (!pFrom || !pTo || !pFrom.amount || !pTo.amount) return false;

    // 检查金额是否匹配（由于可能含有手续费，转出或转入任何一方的绝对金额匹配即可）
    const draftAmt = (draft.postings[1].amount ?? '0').replace(/^-/, '');
    const pFromAbs = pFrom.amount.replace(/^-/, '');
    const pToAbs = pTo.amount.replace(/^-/, '');

    const amtMatch =
      compareDecimals(pFromAbs, draftAmt) === 0 ||
      compareDecimals(pToAbs, draftAmt) === 0;

    return amtMatch;
  });
}

export class BillPipeline {
  /** 串行化锁：整个识别→去重→分类流程互斥，保证并发正确性。 */
  private mutex: Promise<unknown> = Promise.resolve();

  /**
   * 处理一批导入事件。
   * 串行化：多通道并发调用会排队，避免去重/转账识别逻辑错乱。
   */
  process(events: ImportedEvent[], ctx: PipelineContext): Promise<PipelineResult> {
    return this.enqueue(() => this.run(events, ctx));
  }

  /** 实际处理逻辑（无锁，由 process 保证串行）。 */
  private async run(events: ImportedEvent[], ctx: PipelineContext): Promise<PipelineResult> {
    if (events.length === 0) {
      return { drafts: [], duplicates: [], transferCount: 0 };
    }

    logger.info('billPipeline', `开始批次处理账单事件 (事件总数: ${events.length})`);

    // 1. 转账识别（先于去重）
    const { transfers, remaining: afterTransfer } = recognizeTransfers(
      events,
      ctx.transferConfig,
    );

    for (const t of transfers) {
      logger.info('billPipeline', `[转账识别] 命中转账对 (置信度: ${t.confidence}): "${t.draft.narration}", 金额: ${t.draft.postings[1]?.amount}, 原因: ${t.reason}`);
    }

    // 2. 多通道去重（剩余事件之间）
    const { accepted: afterBatchDedup, duplicates: batchDups, duplicateDetails: batchDetails } = batchDedup(
      afterTransfer,
      ctx.dedupConfig,
    );

    for (const detail of batchDetails) {
      const d = detail.event;
      const m = detail.matchedWith;
      const matchText = m ? ` -> 匹配到已有事件 (时间: ${m.time}, 金额: ${m.amount}, 对方: "${m.counterparty || '无'}")` : '';
      logger.info('billPipeline', `[同批次去重] 过滤重复事件 [ID: ${d.id}, 时间: ${d.occurredAt}, 金额: ${d.amount} ${d.currency}, 对方: "${d.counterparty || '无'}"]${matchText} | 原因: ${detail.reason}`);
    }

    // 3. 与历史交易去重（避免重复入账）
    const { accepted: afterHistoryDedup, duplicates: historyDups, duplicateDetails: historyDetails } = dedupAgainstHistory(
      afterBatchDedup,
      ctx.history,
      ctx.dedupConfig,
    );

    for (const detail of historyDetails) {
      const d = detail.event;
      const m = detail.matchedWith;
      const matchText = m ? ` -> 匹配到历史账本 [时间: ${m.time}, 金额: ${m.amount}, 对方: "${m.counterparty || '无'}"]` : '';
      logger.info('billPipeline', `[历史交易去重] 过滤重复事件 [ID: ${d.id}, 时间: ${d.occurredAt}, 金额: ${d.amount} ${d.currency}, 对方: "${d.counterparty || '未知'}"]${matchText} | 原因: ${detail.reason}`);
    }

    // 4. 分类（转账已是完整草稿；非转账事件走 classifyWithCategories）
    const drafts: ProcessedDraft[] = [];
    const transferDups: ImportedEvent[] = [];

    // 4a. 转账草稿（需与历史去重）
    for (const transfer of transfers) {
      if (!isTransferInHistory(transfer, ctx.history)) {
        drafts.push({
          draft: transfer.draft,
          sourceEventIds: [transfer.leftEventId, transfer.rightEventId],
          isTransfer: true,
        });
      } else {
        // 转账已在历史中，将两个源事件标记为重复
        logger.info('billPipeline', `[转账历史去重] 转账草稿 "${transfer.draft.narration}" 已存在于历史账本中，标记丢弃`);
        const leftEvt = events.find(e => e.id === transfer.leftEventId);
        const rightEvt = events.find(e => e.id === transfer.rightEventId);
        if (leftEvt) transferDups.push(leftEvt);
        if (rightEvt) transferDups.push(rightEvt);
      }
    }

    // 4b. 非转账事件
    for (const event of afterHistoryDedup) {
      let result: { draft: TransactionDraft; categoryFallbackReason?: string };

      if (event.sourceAccount && event.categoryAccount) {
        // 预填账户（手动/拍照入口）：直接构造 draft，跳过自动分类
        const { sourceAmount, categoryAmount } = inferPostingAmounts(event);
        result = {
          draft: {
            date: event.occurredAt.slice(0, 10),
            payee: event.counterparty || undefined,
            narration: event.memo || event.counterparty || '手动记账',
            sourceEventIds: [event.id],
            postings: [
              { account: event.sourceAccount, amount: sourceAmount, currency: event.currency },
              { account: event.categoryAccount, amount: categoryAmount, currency: event.currency },
            ],
          },
        };
      } else {
        // 自动分类（CSV/通知/短信/截图入口）
        const classified = classifyWithCategories(event, ctx.rules, ctx.categories, ctx.ledger);
        result = { draft: classified.draft, categoryFallbackReason: classified.categoryFallbackReason };
      }

      drafts.push({
        draft: result.draft,
        sourceEventIds: [event.id],
        isTransfer: false,
        categoryFallbackReason: result.categoryFallbackReason,
      });
      if (result.categoryFallbackReason) {
        logger.debug('billPipeline', `[分类降级] ${event.counterparty || '无'}: ${result.categoryFallbackReason}`);
      }
    }

    const totalDups = batchDups.length + historyDups.length + transferDups.length;
    logger.info('billPipeline', `批次处理完成: 生成 ${drafts.length} 笔草稿 (转账: ${transfers.length}), 过滤 ${totalDups} 笔重复事件`);

    return {
      drafts,
      duplicates: [...batchDups, ...historyDups, ...transferDups],
      duplicateDetails: [...batchDetails, ...historyDetails],
      transferCount: transfers.length,
    };
  }

  /** 串行化执行：保证 process 互斥。 */
  private enqueue<T>(task: () => Promise<T>): Promise<T> {
    const run = this.mutex.then(task, task);
    this.mutex = run.then(() => undefined, () => undefined);
    return run;
  }
}
