/**
 * NDJSON-based event queue
 *
 * 用纯 JS append-only NDJSON 替代 better-sqlite3：
 * - 避免 pkg 单 binary 打包 native module 的坑
 * - 100MB 上限、≤500 行/批的场景下性能完全够用（每次 peek/ack 全文件扫一遍 ~10ms）
 * - 内存 Set 维护已见 rawMessageId 做去重，启动时从文件回放
 */
import * as fs from 'fs';
import { QUEUE_DB_PATH, ensureDirs } from './config';
import { ParsedEvent } from './parsers/types';

const MAX_SIZE_BYTES = 100 * 1024 * 1024;

interface QueueEntry {
  seq: number;
  payload: ParsedEvent;
  createdAt: number;
}

export class EventQueue {
  private entries: QueueEntry[] = [];
  private seen = new Set<string>();
  private nextSeq = 1;
  private filePath: string;

  constructor() {
    ensureDirs();
    // 用 .ndjson 后缀，避免和旧 .db 路径混淆
    this.filePath = QUEUE_DB_PATH.replace(/\.db$/, '.ndjson');
    this.loadFromDisk();
  }

  enqueueBatch(events: ParsedEvent[]): number {
    let inserted = 0;
    const toAppend: string[] = [];
    for (const e of events) {
      if (this.seen.has(e.rawMessageId)) continue;
      const entry: QueueEntry = { seq: this.nextSeq++, payload: e, createdAt: Date.now() };
      this.entries.push(entry);
      this.seen.add(e.rawMessageId);
      toAppend.push(JSON.stringify(entry));
      inserted++;
    }
    if (toAppend.length > 0) {
      fs.appendFileSync(this.filePath, toAppend.join('\n') + '\n', { mode: 0o600 });
    }
    this.evictIfNeeded();
    return inserted;
  }

  peek(limit: number): { ids: number[]; events: ParsedEvent[] } {
    const slice = this.entries.slice(0, limit);
    return { ids: slice.map((e) => e.seq), events: slice.map((e) => e.payload) };
  }

  ack(ids: number[]) {
    if (ids.length === 0) return;
    const idSet = new Set(ids);
    const ackedIds: string[] = [];
    this.entries = this.entries.filter((e) => {
      if (idSet.has(e.seq)) {
        ackedIds.push(e.payload.rawMessageId);
        return false;
      }
      return true;
    });
    // 释放 seen 内存（防长期运行 daemon 累积 rawMessageId Set 无限增长）
    for (const id of ackedIds) this.seen.delete(id);
    this.rewriteFile();
  }

  pending(): number {
    return this.entries.length;
  }

  close() {
    // no-op；append-only 模型不需关闭
  }

  private loadFromDisk() {
    if (!fs.existsSync(this.filePath)) return;
    const raw = fs.readFileSync(this.filePath, 'utf8');
    for (const line of raw.split('\n')) {
      if (!line.trim()) continue;
      try {
        const entry = JSON.parse(line) as QueueEntry;
        this.entries.push(entry);
        this.seen.add(entry.payload.rawMessageId);
        if (entry.seq >= this.nextSeq) this.nextSeq = entry.seq + 1;
      } catch {
        // 损坏行跳过
      }
    }
  }

  private rewriteFile() {
    const tmp = this.filePath + '.tmp';
    const content = this.entries.map((e) => JSON.stringify(e)).join('\n') + (this.entries.length > 0 ? '\n' : '');
    fs.writeFileSync(tmp, content, { mode: 0o600 });
    fs.renameSync(tmp, this.filePath);
  }

  private evictIfNeeded() {
    try {
      const size = fs.statSync(this.filePath).size;
      if (size <= MAX_SIZE_BYTES) return;
      // 删最老 10%
      const dropCount = Math.max(100, Math.floor(this.entries.length * 0.1));
      for (let i = 0; i < dropCount && this.entries.length > 0; i++) {
        const e = this.entries.shift()!;
        this.seen.delete(e.payload.rawMessageId);
      }
      this.rewriteFile();
    } catch {
      // ignore
    }
  }
}
