import { compareDecimals, negateDecimal } from '../core/decimal';
import { matchCategory, resolveCategoryAccount, type Category } from '../taxonomy/categories';
import type { Classification, Direction, ImportedEvent, LedgerIndex, Rule, TransactionDraft } from '../core/types';
import { generateRemark } from './remarkTemplate';
import { ACCOUNT_NAME_MAPPINGS, ACCOUNT_NAME_CONTAINS_MAPPINGS, LIABILITY_ACCOUNT_PATTERN, HUABEI_REPAYMENT_ACCOUNT, CREDIT_CARD_REPAYMENT_PATTERN } from './ruleConfig';

/** 根据事件方向推断 posting 金额符号（正数=绝对金额）。 */
export function inferPostingAmounts(event: ImportedEvent): { sourceAmount: string; categoryAmount: string } {
  const amount = event.amount.startsWith('-') ? event.amount.slice(1) : event.amount;
  const sourceAmount = event.direction === 'income' || event.direction === 'refund' ? amount : negateDecimal(amount);
  const categoryAmount = negateDecimal(sourceAmount);
  return { sourceAmount, categoryAmount };
}

function getNarration(ruleNarration: string | undefined, event: ImportedEvent): string {
  if (ruleNarration) {
    return ruleNarration.includes('【') ? generateRemark(ruleNarration, event) : ruleNarration;
  }
  const main = event.counterparty || event.memo;
  if (!main) return '导入账单';
  if (event.counterparty && event.memo) {
    return generateRemark('【商户名称】 - 【商品名称】', event);
  }
  return main;
}

const contains = (input: string, query?: string) => !query || input.toLowerCase().includes(query.toLowerCase());

/** 静态条件预筛选（规则匹配共用）。 */
export function matchesStaticConditions(
  event: ImportedEvent,
  opts: { direction?: Direction; currency?: string; counterpartyContains?: string; memoContains?: string; counterpartyNotContains?: string; memoNotContains?: string; minAmount?: string; maxAmount?: string },
): boolean {
  if (opts.direction && opts.direction !== event.direction) return false;
  if (opts.currency && opts.currency !== event.currency) return false;
  if (!contains(event.counterparty, opts.counterpartyContains)) return false;
  if (!contains(event.memo, opts.memoContains)) return false;
  // 否定条件：包含则不命中
  if (opts.counterpartyNotContains && event.counterparty.toLowerCase().includes(opts.counterpartyNotContains.toLowerCase())) return false;
  if (opts.memoNotContains && event.memo.toLowerCase().includes(opts.memoNotContains.toLowerCase())) return false;

  const absAmount = event.amount.startsWith('-') ? event.amount.slice(1) : event.amount;
  if (opts.minAmount && compareDecimals(absAmount, opts.minAmount) < 0) return false;
  if (opts.maxAmount && compareDecimals(absAmount, opts.maxAmount) > 0) return false;

  return true;
}

function matches(event: ImportedEvent, rule: Rule): boolean {
  return matchesStaticConditions(event, rule);
}
function first(row: Record<string, string>, keys: string[]): string {
  for (const k of keys) {
    if (row[k] !== undefined) return row[k];
  }
  return '';
}

function sanitizeAccountSegment(name: string): string {
  // 1. 将括号、中括号和空格替换为连字符 -
  let cleaned = name.replace(/[()（）[\]\s]/g, '-');
  // 2. 将连续的多个连字符合并为一个
  cleaned = cleaned.replace(/-+/g, '-');
  // 3. 移除首尾的连字符
  cleaned = cleaned.replace(/^-|-$/g, '');
  // 4. 仅保留字母、数字、中文和连字符 -
  cleaned = cleaned.replace(/[^a-zA-Z0-9\u4e00-\u9fa5-]/g, '');
  // 5. 再次合并并移除首尾的连字符（防止非字符被移去后产生连续/首尾的连字符）
  cleaned = cleaned.replace(/-+/g, '-').replace(/^-|-$/g, '');
  return cleaned.trim();
}

export function resolveSourceAccount(
  event: ImportedEvent,
  rule?: Rule,
): string {
  // 1. 规则指定的账户（最高优先级）
  if (rule?.sourceAccount) return rule.sourceAccount;

  // 2. raw 数据中的支付方式（如 "招商银行(2586)" → Assets:招商银行-2586）
  const raw = event.raw || {};
  let method = first(raw, ['收/付款方式', '收付款方式', '支付方式', 'Payment Method', 'method']) || '';

  if (method) {
    method = method.split('&')[0].trim();

    if (method === '零钱') {
      method = '微信零钱';
    } else if (method === '余额' || method === '账户余额') {
      method = '支付宝余额';
    } else if (method.includes('花呗')) {
      method = '支付宝花呗';
    }
    method = sanitizeAccountSegment(method);
    if (method) {
      if (LIABILITY_ACCOUNT_PATTERN.test(method)) {
        return `Liabilities:${method}`;
      }
      return `Assets:${method}`;
    }
  }

  // 3. 兜底
  return 'Assets:Unknown';
}

/**
 * 双轨制分类记账（plan.md「决策 1」）。
 *
 * 与 {@link classify} 的区别：
 * - 优先级：规则匹配 > 分类匹配（关键词/模糊）> 未分类兜底
 * - 分类通过 {@link Category.linkedAccount} 映射到 Beancount 账户（双轨制）
 * - 账户不存在时不降级，由导入流程自动 open
 *
 * @param event 待分类的导入事件
 * @param rules 规则列表（优先匹配）
 * @param categories 分类库（规则未命中时用关键词/模糊匹配）
 * @param ledger 账本（校验账户存在性）
 */
export function classifyWithCategories(
  event: ImportedEvent,
  rules: Rule[],
  categories: Category[],
  ledger: LedgerIndex,
): Classification & { categoryId?: string; categoryFallbackReason?: string } {
  // 1. 先尝试规则匹配
  const sorted = [...rules].sort((a, b) => b.priority - a.priority);
  const rule = sorted.find(item => matches(event, item));
  const { sourceAmount, categoryAmount } = inferPostingAmounts(event);

  // 资金来源账户（与 classify 一致）
  const sourceAccountRaw = resolveSourceAccount(event, rule);
  const sourceValid = ledger.accounts.has(sourceAccountRaw);

  // 分类账户：规则优先，否则用分类库匹配
  let categoryAccount = '';
  let categoryId: string | undefined;
  let categoryFallbackReason: string | undefined;
  let categoryValid = false;

  if (rule) {
    // 规则命中：直接用规则的 categoryAccount，不降级
    categoryAccount = rule.categoryAccount;
    categoryValid = ledger.accounts.has(categoryAccount);
    if (!categoryValid) {
      categoryFallbackReason = `账户 ${categoryAccount} 尚未开户，将在导入时自动创建`;
    }
  } else {
    // 规则未命中：用分类库匹配
    // refund 视为 income（退款是资金回流），fee 视为 expense
    // transfer 未配对时使用中性待确认账户，不强行归为 expense
    if (event.direction === 'transfer') {
      categoryAccount = 'Equity:待确认转账';
      categoryValid = false;
      categoryFallbackReason = '转账事件未配对，请手动确认目标账户';
    }
    const isIncome = event.direction === 'income' || event.direction === 'refund';
    const type: 'income' | 'expense' = isIncome ? 'income' : 'expense';

    if (!categoryAccount) {
    // 智能还款识别（花呗还款/信用卡还款属于负债偿还，目标账户为 Liabilities）
    const fullText = [event.raw?.text, event.counterparty, event.memo].filter(Boolean).join(' ');
    let isRepayment = false;
    if (event.direction !== 'refund' && /还款/.test(fullText)) {
      if (/花呗/.test(fullText)) {
        categoryAccount = HUABEI_REPAYMENT_ACCOUNT;
        categoryValid = true;
        isRepayment = true;
      } else {
        const cardMatch = fullText.match(CREDIT_CARD_REPAYMENT_PATTERN);
        if (cardMatch) {
          const cardName = sanitizeAccountSegment(cardMatch[1]);
          categoryAccount = `Liabilities:${cardName}`;
          categoryValid = true;
          isRepayment = true;
        }
      }
    }

    if (!isRepayment) {
      const candidates = categories.filter(c => c.type === type);
      // 分字段匹配优先：先分别匹配 counterparty 和 memo，避免跨字段拼接误匹配
      // 仅 exact/keyword/fuzzy 视为有效命中；fallback（"其他"）不阻止后续字段匹配
      const isRealMatch = (m: { matchedBy: string }) => m.matchedBy !== 'none' && m.matchedBy !== 'fallback';
      let match = matchCategory(event.counterparty || '', candidates);
      if (!isRealMatch(match) && event.memo) {
        const memoMatch = matchCategory(event.memo, candidates);
        if (isRealMatch(memoMatch)) match = memoMatch;
      }
      // 两者都未有效命中时，再用拼接文本兜底
      if (!isRealMatch(match) && event.counterparty && event.memo) {
        const combinedMatch = matchCategory(`${event.counterparty} ${event.memo}`, candidates);
        if (isRealMatch(combinedMatch)) match = combinedMatch;
      }
      const resolved = resolveCategoryAccount(match.category, type, ledger);
      categoryAccount = resolved.account;
      categoryValid = resolved.valid;
      categoryId = match.category?.id;
      if (!resolved.valid) categoryFallbackReason = resolved.fallbackReason;
    }
    } // end if (!categoryAccount)
  }

  const narration = getNarration(rule?.narration, event);
  const draft: TransactionDraft = {
    date: event.occurredAt.slice(0, 10),
    payee: event.counterparty || undefined,
    narration,
    tags: rule?.tags,
    sourceEventIds: [event.id],
    metadata: {
      import_id: event.id,
    },
    postings: [
      { account: sourceAccountRaw, amount: sourceAmount, currency: event.currency },
      { account: categoryAccount, amount: categoryAmount, currency: event.currency },
    ],
  };

  const known = sourceValid && categoryValid;
  return {
    draft,
    ruleId: rule?.id,
    confidence: known ? 'high' : 'low',
    categoryId,
    categoryFallbackReason,
  };
}

/** A transfer is deliberately kept as a candidate until the user confirms both legs. */
export function classifyTransferPair(left: ImportedEvent, right: ImportedEvent, leftAccount: string, rightAccount: string): TransactionDraft {
  const leftAbs = left.amount.startsWith('-') ? left.amount.slice(1) : left.amount;
  const rightAbs = right.amount.startsWith('-') ? right.amount.slice(1) : right.amount;
  if (left.currency !== right.currency || compareDecimals(leftAbs, rightAbs) !== 0) {
    throw new Error('转账候选金额或币种不一致');
  }
  const amount = leftAbs;
  const from = left.amount.startsWith('-') ? leftAccount : rightAccount;
  const to = left.amount.startsWith('-') ? rightAccount : leftAccount;
  return {
    date: (left.occurredAt < right.occurredAt ? left.occurredAt : right.occurredAt).slice(0, 10),
    narration: `转账：${from} → ${to}`,
    sourceEventIds: [left.id, right.id],
    metadata: {
      import_id: `${left.id},${right.id}`,
    },
    postings: [
      { account: from, amount: `-${amount}`, currency: left.currency },
      { account: to, amount, currency: left.currency },
    ],
  };
}

