/**
 * 短信监听（plan.md「4.2 短信监听服务」+「4.3 短信解析器」）。
 *
 * 参考 AutoAccounting 的 SmsReceiver（含关键词过滤）。
 * 原生监听通过 Config Plugin 注入，此处定义桥接 + 解析器（纯函数，可单测）。
 */

import { detectDirection, SMS_BANK_KEYWORDS } from '../domain/core/constants';
import { hash } from '../domain/core/ledger';
import { passesKeywordFilter, type KeywordFilter } from '../domain/rules/keywordFilter';
import type { ImportedEvent } from '../domain/core/types';
import { logger } from '../utils/logger';

/** 短信事件（原生监听器推送）。 */
export interface SmsEvent {
  sender: string;
  body: string;
  timestamp: number;
}

/** 原生短信监听器契约（Config Plugin 注入）。 */
export interface SmsListener {
  start(handler: (event: SmsEvent) => void): Promise<void>;
  stop(): Promise<void>;
}

/**
 * 短信解析器（plan.md「4.3」）：从短信正文提取账单信息。
 */
export function parseSms(event: SmsEvent): ImportedEvent | null {
  const body = event.body;

  // 金额提取（支持 金额/消费/支出/收入/转账/退款 + 直接 数字元）
  const amountMatch = body.match(/(?:金额|消费|支出|收入|转账|退款|扣款)\s*(?:人民币)?\s*(\d+(?:\.\d+)?)\s*元/)
    ?? body.match(/(\d+(?:\.\d+)?)\s*元/);
  if (!amountMatch) return null;

  // 使用字符串保持精度，避免 parseFloat 精度丢失
  const amountStr = amountMatch[1];

  // 卡号提取
  const cardMatch = body.match(/尾号\s*(\d{4})/);
  const card = cardMatch?.[1] ?? '';

  // 时间提取（部分银行短信含时间，否则用接收时间）
  const timeMatch = body.match(/(\d{1,2}月\d{1,2}日\s*\d{1,2}:\d{2})/);
  const occurredAt = timeMatch ? normalizeSmsTime(timeMatch[1]) : new Date(event.timestamp).toISOString().slice(0, 16);

  // 方向判断
  const direction = detectDirection(body);

  return {
    id: `sms:${hash(event.sender + body)}`,
    occurredAt,
    amount: direction === 'expense' ? `-${amountStr}` : amountStr,
    currency: 'CNY',
    direction,
    counterparty: extractCounterparty(body),
    memo: card ? `银行卡尾号${card}` : '银行短信',
    raw: { sender: event.sender, body },
  };
}

function extractCounterparty(body: string): string {
  // 提取"于XXX消费"或"在XXX"的商户
  const m = body.match(/(?:于|在)\s*([^\s,，。]+?)\s*(?:消费|支出|支付|购物)/);
  return m?.[1] ?? '';
}

function normalizeSmsTime(time: string): string {
  // "7月13日 14:30" → "2026-07-13 14:30"（用当前年，处理跨年边界）
  let year = new Date().getFullYear();
  const m = time.match(/(\d{1,2})月(\d{1,2})日\s*(\d{1,2}):(\d{2})/);
  if (!m) return time;
  const [, month, day, hour, minute] = m;
  const parsedMonth = parseInt(month, 10);
  const currentMonth = new Date().getMonth() + 1; // 1-indexed

  // 跨年边界判定：如果短信月份比当前月份大 3 以上，说明是去年的短信
  // 例如：当前 1 月，短信 11 月 → 去年 11 月
  if (parsedMonth > currentMonth + 3) {
    year--;
  }
  return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')} ${hour.padStart(2, '0')}:${minute}`;
}

/** 判断是否为银行短信（plan.md「4.2 isBankSms」）。 */
export function isBankSms(body: string): boolean {
  return SMS_BANK_KEYWORDS.some(kw => body.includes(kw));
}

/**
 * 短信通道协调器：银行短信过滤 + 关键词过滤 + 去重 + 解析。
 */
export class SmsChannel {
  private md5Hashes = new Set<string>();
  private handler: ((event: ImportedEvent, raw: SmsEvent) => void) | null = null;

  /** md5Hashes 最大容量，超出时淘汰最旧条目。 */
  private static MAX_HASHES = 1000;

  constructor(
    private readonly nativeListener: SmsListener | null = null,
    private keywordFilter: KeywordFilter | null = null,
  ) {}

  onBillDetected(handler: (event: ImportedEvent, raw: SmsEvent) => void): void {
    this.handler = handler;
  }

  async start(): Promise<void> {
    if (!this.nativeListener) return;
    await this.nativeListener.start(e => this.handleSms(e));
  }

  async stop(): Promise<void> {
    await this.nativeListener?.stop();
  }

  handleSms(event: SmsEvent): { processed: boolean; reason?: string } {
    // 1. 银行短信过滤
    if (!isBankSms(event.body)) {
      logger.debug('sms', `[短信过滤] 非银行短信: ${event.sender}`);
      return { processed: false, reason: '非银行短信' };
    }

    // 2. 关键词过滤
    if (this.keywordFilter && !passesKeywordFilter(event.body, this.keywordFilter)) {
      logger.debug('sms', `[短信过滤] 关键词未命中: ${event.sender}`);
      return { processed: false, reason: '关键词过滤' };
    }

    // 3. MD5 去重（带容量上限）
    const md5 = hash(event.sender + event.body);
    if (this.md5Hashes.has(md5)) {
      logger.debug('sms', `[短信去重] MD5 重复: ${event.sender}`);
      return { processed: false, reason: 'MD5 去重' };
    }
    this.md5Hashes.add(md5);
    // LRU 淘汰：超出容量时删除最早插入的条目
    if (this.md5Hashes.size > SmsChannel.MAX_HASHES) {
      const oldest = this.md5Hashes.values().next().value;
      if (oldest) this.md5Hashes.delete(oldest);
    }

    // 4. 解析
    const bill = parseSms(event);
    if (bill && this.handler) {
      this.handler(bill, event);
    } else if (!bill) {
      logger.debug('sms', `[短信解析] 未命中账单规则: ${event.sender}`);
    }
    return { processed: !!bill, reason: bill ? undefined : '解析未命中' };
  }

  reset(): void {
    this.md5Hashes.clear();
  }
}

/** 模拟短信监听器（测试用）。 */
export class SimulatedSmsListener implements SmsListener {
  private handler: ((e: SmsEvent) => void) | null = null;
  async start(handler: (e: SmsEvent) => void): Promise<void> { this.handler = handler; }
  async stop(): Promise<void> { this.handler = null; }
  simulate(event: SmsEvent): void { this.handler?.(event); }
}
