import { create } from 'zustand';
import { parseLedger, getDefaultCurrency, serializeTransaction } from '../domain/core/ledger';
import { createMobileStore, MemoryBackend, type MobileBeanBackend, type MobileBeanStore } from '../domain/platform/mobileStore';
import { checkDuplicate, type DedupConfig } from '../domain/transaction/dedup';
import { toDateString } from '../domain/core/decimal';
import type { LedgerFile, LedgerIndex, TransactionDraft } from '../domain/core/types';
import type { MobileCommit } from '../domain/platform/mobile';
import { removeProcessedTxKey } from '../services/security/txKeyStore';
import { logger } from '../utils/logger';

/**
 * 账本状态管理（plan.md「0.1 状态管理」）。
 *
 * 职责：
 * - 加载/解析 .bean 文件，维护 LedgerIndex
 * - 通过 MobileBeanStore 提供并发安全的 main.bean 写入
 * - 缓存解析结果（数据库缓存在 storage 层，这里维护内存索引）
 *
 * 持久化后端通过注入，使本 store 可单测（MemoryBackend）。
 */

export interface LedgerState {
  /** 当前账本索引（解析结果）。 */
  ledger: LedgerIndex | null;
  /** main.bean 写入存储（并发安全）。 */
  mobileStore: MobileBeanStore | null;
  /** main.bean 当前内容（便于 UI 直接读取）。 */
  mobileBean: string;
  isLoading: boolean;
  error: string | null;

  // actions
  /** 加载账本文件并初始化 main.bean 存储。 */
  loadLedger: (files: LedgerFile[], backend?: MobileBeanBackend) => Promise<void>;
  /** 追加一笔交易到 main.bean（并发安全，含去重检查）。 */
  addTransaction: (draft: TransactionDraft, dedupConfig?: DedupConfig, force?: boolean) => Promise<MobileCommit>;
  /** 刷新账本（重新解析，用于文件变更后）。 */
  refresh: () => Promise<void>;
  /** 直接替换 main.bean 内容（恢复/导入场景）。 */
  replaceMobileBean: (content: string) => Promise<void>;
  /** 自动开户（在 main.bean 中插入 open 指令并重解析）。 */
  autoOpenAccounts: (accounts: string[]) => Promise<void>;
  /** 自动销户（在 main.bean 中插入 close 指令并重解析）。 */
  autoCloseAccount: (accountName: string) => Promise<void>;
  /** 调整账户余额（写入 pad & balance 对并重解析）。 */
  adjustAccountBalance: (accountName: string, balance: string, date: string) => Promise<void>;
  /** 批量追加多笔交易（在锁内拼接+写入，避免 TOCTOU 竞态）。 */
  appendTransactionsBatch: (drafts: TransactionDraft[]) => Promise<void>;
  /** 编辑交易：用新 raw 替换旧 raw，返回新交易 id。 */
  editTransaction: (oldRaw: string, newRaw: string) => Promise<string>;
  /** 删除交易：从 mainBean 中移除指定 raw 块。 */
  deleteTransaction: (raw: string) => Promise<void>;
  /** 撤销删除：将原始 raw 文本重新追加回 mobile.bean */
  restoreTransaction: (raw: string) => Promise<void>;
  /** 重置到初始状态。 */
  reset: () => void;
}

/** 当前加载的账本文件（用于 refresh）。 */
let loadedFiles: LedgerFile[] = [];

/** 重新解析 mainBean 内容为 LedgerIndex。 */
function reparseLedger(mainBean: string): LedgerIndex {
  const files: LedgerFile[] = [{ path: 'main.bean', content: mainBean }];
  for (const f of loadedFiles) {
    if (f.path !== 'main.bean' && f.path !== 'mobile.bean') files.push(f);
  }
  return parseLedger(files);
}

/**
 * 异步互斥锁：保证所有修改 mainBean 的操作（read-modify-write）串行执行。
 *
 * 问题：autoOpenAccounts/adjustAccountBalance 等在 zustand 层读 get().mobileBean
 * → 拼接 → mobileStore.replace()。mobileStore.enqueue 只序列化最终写入，
 * 不保护读取。并发时后写入的 replace 会用旧快照覆盖，丢失交易。
 *
 * withLock 把整个 read-modify-write 包在一个 Promise 链里，确保不交错。
 */
let writeLock: Promise<unknown> = Promise.resolve();
/** 代际计数器：reset 后旧链上的 pending task 因 generation 不匹配而跳过 set。 */
let lockGeneration = 0;
function withLock<T>(task: (gen: number) => Promise<T>): Promise<T> {
  const gen = lockGeneration;
  const result = writeLock.then(() => task(gen), () => task(gen));
  writeLock = result.then(() => undefined, () => undefined);
  return result;
}


/** 从 raw 文本提取 dr-id，用于精确定位交易块。 */
function extractDrId(raw: string): string | null {
  const m = raw.match(/^\s+dr-id:\s*"([^"]+)"/m);
  return m ? m[1] : null;
}

/** 在 bean 内容中找到包含指定 dr-id 的交易块的起始位置。 */
function findTxBlockByDrId(content: string, drId: string): number {
  const marker = `  dr-id: "${drId}"`;
  const markerIdx = content.indexOf(marker);
  if (markerIdx === -1) return -1;
  // 向前找到交易头（日期开头的行）
  let start = markerIdx;
  while (start > 0 && content[start - 1] !== '\n') start--;
  while (start > 0) {
    const lineStart = content.lastIndexOf('\n', start - 2) + 1;
    const line = content.slice(lineStart, start - 1);
    if (/^\d{4}-\d{2}-\d{2}\s+[*!]/.test(line)) return lineStart;
    if (line.trim() === '' || !line.startsWith(' ')) break;
    start = lineStart;
  }
  return content.lastIndexOf('\n', markerIdx - 1) + 1;
}

/**
 * 从交易 raw 文本重建去重键（与 automationPipeline 的 txKey 格式一致）。
 *
 * txKey 格式：`${YYYY-MM-DD}_${amount}_${counterparty}`
 *   - date：raw 首行的日期（YYYY-MM-DD，10 字符）
 *   - amount：posting 行金额去负号（与识别时 event.amount 一致的正数字符串）
 *   - counterparty：首行 payee 去引号（与识别时 event.counterparty 一致）
 *
 * 用于删除交易时清除去重缓存，使「删了重记」流程不被拦截。
 * 若无法提取（格式异常），返回 null，调用方应安全跳过。
 */
function buildTxKeyFromRaw(raw: string): string | null {
  // 1. 首行提取日期 + payee
  //    格式：2026-07-24 * "盛香亭新式热卤" "narration"
  const header = raw.match(/^(\d{4}-\d{2}-\d{2})\s+[*!](?:\s+"((?:[^"\\]|\\.)*)")?/);
  if (!header) return null;
  const date = header[1];
  const payee = header[2] ?? '';

  // 2. 从第一条带金额的 posting 行提取金额（去负号、去币种）
  //    格式：  Assets:花呗  -66.8 CNY
  let amount = '';
  for (const line of raw.split('\n')) {
    if (!line.startsWith('  ') || line.includes('dr-id:') || line.includes(': "')) continue;
    const m = line.match(/\s(-?\d[\d,]*(?:\.\d+)?)\s+[A-Z]{3}/);
    if (m) {
      amount = m[1].replace(/,/g, '').replace(/^-/, '');
      break;
    }
  }
  if (!amount) return null;

  return `${date}_${amount}_${payee}`;
}

export const useLedgerStore = create<LedgerState>((set, get) => ({
  ledger: null,
  mobileStore: null,
  mobileBean: '',
  isLoading: false,
  error: null,

  loadLedger: async (files, backend) => {
    set({ isLoading: true, error: null });
    try {
      loadedFiles = files;
      const mobileStore = createMobileStore(backend ?? new MemoryBackend());
      await mobileStore.init();
      const content = mobileStore.getContent();

      // 如果 main.bean 是空的，并且 files 里有初始加载内容（比如 Onboarding 的 SAMPLE_LEDGER），则使用它作为初始值
      let finalContent = content;
      if (!content.trim() && files && files.length > 0) {
        finalContent = files[0].content;
        await mobileStore.replace(finalContent);
      }

      const ledger = parseLedger([{ path: 'main.bean', content: finalContent }]);
      set({
        ledger,
        mobileStore,
        mobileBean: finalContent,
        isLoading: false,
      });
    } catch (e) {
      set({ isLoading: false, error: e instanceof Error ? e.message : String(e) });
      throw e;
    }
  },

  addTransaction: async (draft, dedupConfig?: DedupConfig, force?: boolean) => {
    return withLock(async (gen) => {
      const { ledger, mobileStore } = get();
      if (!ledger || !mobileStore) throw new Error('账本未加载，请先调用 loadLedger');

      // 去重检查：防止手动录入与已有交易重复
      if (!force) {
        const amount = draft.postings[0]?.amount ?? '0';
        const existingForDedup = ledger.transactions
          .filter(t => t.postings.length >= 2)
          .map(t => ({
            time: t.date,
            amount: t.postings[0]?.amount ?? '0',
            counterparty: t.payee ?? '',
          }));
        const dedupResult = checkDuplicate(
          { id: 'manual', occurredAt: draft.date, amount, currency: draft.postings[0]?.currency ?? 'CNY', direction: 'expense', counterparty: draft.payee ?? '', memo: draft.narration, raw: {} },
          existingForDedup,
          dedupConfig,
        );
        if (dedupResult.isDuplicate) {
          throw new Error(`可能与已有交易重复：${dedupResult.reason ?? '时间、金额、账户匹配'}（置信度：${dedupResult.confidence}）`);
        }
      }

      const commit = await mobileStore.append(draft, ledger);
      const mobileBean = commit.content;
      const newLedger = reparseLedger(mobileBean);
      
      // 详细记录入账信息日志
      logger.info('ledgerStore', `确认入账成功 [日期: ${draft.date}, 商户/交易对手: ${draft.payee ?? '无'}, 叙述: ${draft.narration}, 金额: ${draft.postings[0]?.amount ?? '0'} ${draft.postings[0]?.currency ?? 'CNY'}, 账户: ${draft.postings[0]?.account}]`);

      if (gen !== lockGeneration || get().mobileStore !== mobileStore) return commit;
      set({ mobileBean, ledger: newLedger });
      return commit;
    });
  },

  refresh: async () => {
    return withLock(async (gen) => {
      const { mobileStore } = get();
      if (!mobileStore) return;
      await mobileStore.reload();
      const mobileBean = mobileStore.getContent();
      const ledger = reparseLedger(mobileBean);
      if (gen !== lockGeneration || get().mobileStore !== mobileStore) return;
      set({ ledger, mobileBean });
    });
  },

  replaceMobileBean: async (content) => {
    return withLock(async (gen) => {
      const { mobileStore } = get();
      if (!mobileStore) throw new Error('账本未加载');
      logger.info('ledgerStore', `正在全量替换 mobile.bean 内容 (新长度: ${content.length} 字节)`);
      await mobileStore.replace(content);
      const ledger = reparseLedger(content);
      if (gen !== lockGeneration || get().mobileStore !== mobileStore) return;
      set({ mobileBean: content, ledger });
    });
  },

  autoOpenAccounts: async (accounts) => {
    return withLock(async (gen) => {
      const { mobileStore } = get();
      if (!mobileStore) throw new Error('账本未加载');
      const ledger = get().ledger;
      // 去重：跳过已 open 的账户
      const newAccounts = accounts.filter(acc => !ledger?.accounts.has(acc));
      if (newAccounts.length === 0) return;
      logger.info('ledgerStore', `自动开户 ${newAccounts.length} 个账户: ${newAccounts.join(', ')}`);
      const today = new Date().toISOString().slice(0, 10);
      const currency = getDefaultCurrency(ledger);
      const openStatements = newAccounts.map(acc => `${today} open ${acc} ${currency}`).join('\n');
      await mobileStore.modifyContent(current => `${current.trimEnd()}\n\n${openStatements}\n`.trim());
      const newLedger = reparseLedger(mobileStore.getContent());
      if (gen !== lockGeneration || get().mobileStore !== mobileStore) return;
      set({ mobileBean: mobileStore.getContent(), ledger: newLedger });
    });
  },

  autoCloseAccount: async (accountName) => {
    return withLock(async (gen) => {
      const { mobileStore } = get();
      if (!mobileStore) throw new Error('账本未加载');
      logger.info('ledgerStore', `自动销户账户: ${accountName}`);
      const today = new Date().toISOString().slice(0, 10);
      await mobileStore.modifyContent(current => `${current.trimEnd()}\n\n${today} close ${accountName}\n`.trim());
      const newLedger = reparseLedger(mobileStore.getContent());
      if (gen !== lockGeneration || get().mobileStore !== mobileStore) return;
      set({ mobileBean: mobileStore.getContent(), ledger: newLedger });
    });
  },

  adjustAccountBalance: async (accountName, balance, date) => {
    return withLock(async (gen) => {
      const { mobileStore } = get();
      if (!mobileStore) throw new Error('账本未加载');
      const ledger = get().ledger;
      const currency = getDefaultCurrency(ledger);
      logger.info('ledgerStore', `校准账户余额: ${accountName} -> ${balance} ${currency} (日期: ${date})`);
      // 计算前一天的日期用于 pad（使用本地时间避免 UTC 偏移）
      const [y, m, d] = date.split('-').map(Number);
      const padDate = toDateString(new Date(y, m - 1, d - 1));
      const statements = `${padDate} pad ${accountName} Equity:Opening-Balances\n${date} balance ${accountName} ${balance} ${currency}`;
      await mobileStore.modifyContent(current => `${current.trimEnd()}\n\n${statements}\n`.trim());
      const newLedger = reparseLedger(mobileStore.getContent());
      if (gen !== lockGeneration || get().mobileStore !== mobileStore) return;
      set({ mobileBean: mobileStore.getContent(), ledger: newLedger });
    });
  },

  appendTransactionsBatch: async (drafts) => {
    return withLock(async (gen) => {
      const { mobileStore } = get();
      if (!mobileStore) throw new Error('账本未加载');
      logger.info('ledgerStore', `批量追加 ${drafts.length} 笔交易到 mobile.bean`);
      const mobileBean = mobileStore.getContent(); // 从存储读取，避免 zustand state 与存储不一致
      const serializedChunk = drafts.map(d => serializeTransaction(d)).join('\n\n');
      const newContent = `${mobileBean.trimEnd()}\n\n${serializedChunk}\n`.trim();
      await mobileStore.replace(newContent);
      const newLedger = reparseLedger(newContent);
      if (gen !== lockGeneration || get().mobileStore !== mobileStore) return;
      set({ mobileBean: newContent, ledger: newLedger });
    });
  },

  editTransaction: async (oldRaw, newRaw) => {
    return withLock(async (gen) => {
      const { mobileStore } = get();
      if (!mobileStore) throw new Error('账本未加载');
      const mobileBean = mobileStore.getContent();
      if (!mobileBean.includes(oldRaw)) throw new Error('交易未找到，可能已被删除或修改');
      const drId = extractDrId(oldRaw);
      let idx = drId ? findTxBlockByDrId(mobileBean, drId) : -1;
      if (idx === -1) idx = mobileBean.lastIndexOf(oldRaw);
      const newContent = mobileBean.slice(0, idx) + newRaw + mobileBean.slice(idx + oldRaw.length);
      await mobileStore.replace(newContent);
      const newLedger = reparseLedger(newContent);
      logger.info('ledgerStore', `修改已保存交易成功:\n【修改前】:\n${oldRaw.trim()}\n【修改后】:\n${newRaw.trim()}`);
      if (gen !== lockGeneration || get().mobileStore !== mobileStore) return '';
      set({ mobileBean: newContent, ledger: newLedger });
      const newTx = newLedger.transactions.find(t => t.raw === newRaw);
      return newTx?.id ?? '';
    });
  },

  deleteTransaction: async (raw) => {
    return withLock(async (gen) => {
      const { mobileStore } = get();
      if (!mobileStore) throw new Error('账本未加载');
      const mobileBean = mobileStore.getContent();
      if (!mobileBean.includes(raw)) throw new Error('交易未找到，可能已被删除或修改');
      const drId = extractDrId(raw);
      let idx = drId ? findTxBlockByDrId(mobileBean, drId) : -1;
      if (idx === -1) idx = mobileBean.lastIndexOf(raw);
      let newContent = mobileBean.slice(0, idx) + mobileBean.slice(idx + raw.length);
      newContent = newContent.replace(/\n{3,}/g, '\n\n').trim();
      await mobileStore.replace(newContent);
      const newLedger = reparseLedger(newContent);
      logger.info('ledgerStore', `删除已保存交易成功:\n【被删除交易内容】:\n${raw.trim()}`);
      // 清除去重键，允许用户「删了重记」（删除=这笔不算了，理应可重新识别）
      const txKey = buildTxKeyFromRaw(raw);
      if (txKey) {
        removeProcessedTxKey(txKey);
        logger.info('ledgerStore', `已清除去重键，允许重新识别: ${txKey}`);
      }
      if (gen !== lockGeneration || get().mobileStore !== mobileStore) return;
      set({ mobileBean: newContent, ledger: newLedger });
    });
  },

  restoreTransaction: async (raw) => {
    return withLock(async (gen) => {
      const { mobileStore } = get();
      if (!mobileStore) throw new Error('账本未加载');
      const mobileBean = mobileStore.getContent();
      if (mobileBean.includes(raw)) return;
      const newContent = (mobileBean.trimEnd() + '\n\n' + raw.trim() + '\n').trim();
      await mobileStore.replace(newContent);
      const newLedger = reparseLedger(newContent);
      logger.info('ledgerStore', '恢复交易成功: ' + raw.trim());
      if (gen !== lockGeneration || get().mobileStore !== mobileStore) return;
      set({ mobileBean: newContent, ledger: newLedger });
    });
  },
  reset: () => {
    loadedFiles = [];
    lockGeneration++;
    writeLock = Promise.resolve();
    set({ ledger: null, mobileStore: null, mobileBean: '', isLoading: false, error: null });
  },
}));
