import { createHash, randomUUID } from 'crypto';
import {
  mkdirSync,
  readFileSync,
  writeFileSync,
  existsSync,
  renameSync,
  appendFileSync,
} from 'fs';
import { dirname, join, resolve } from 'path';
import type { ListenerConfig, SwiftListenerEvent } from '../types';

function ensureDir(path: string): void {
  mkdirSync(path, { recursive: true });
}

export class EventStore {
  private processed = new Set<string>();

  constructor(
    private readonly projectRoot: string,
    private readonly config: ListenerConfig
  ) {
    this.loadProcessedIndex();
  }

  private paths() {
    const s = this.config.storage;
    return {
      eventsDir: resolve(this.projectRoot, s.eventsDir),
      latestStatus: resolve(this.projectRoot, s.latestStatus),
      processedIndex: resolve(this.projectRoot, s.processedIndex),
      inboxDir: resolve(this.projectRoot, s.inboxDir),
      archiveDir: resolve(this.projectRoot, s.archiveDir),
    };
  }

  private loadProcessedIndex(): void {
    const { processedIndex } = this.paths();
    if (!existsSync(processedIndex)) return;
    try {
      const data = JSON.parse(readFileSync(processedIndex, 'utf8')) as { keys?: string[] };
      for (const k of data.keys ?? []) this.processed.add(k);
    } catch {
      // fresh index on parse failure
    }
  }

  private persistProcessedIndex(): void {
    const { processedIndex } = this.paths();
    ensureDir(dirname(processedIndex));
    writeFileSync(processedIndex, JSON.stringify({ keys: [...this.processed].slice(-50000) }, null, 2));
  }

  dedupeKey(source: string, payload: string): string {
    return createHash('sha256').update(`${source}\n${payload}`).digest('hex');
  }

  isProcessed(key: string): boolean {
    return this.processed.has(key);
  }

  saveEvent(event: SwiftListenerEvent): void {
    const { eventsDir, latestStatus } = this.paths();
    ensureDir(eventsDir);
    const eventPath = join(eventsDir, `${event.id}.json`);
    writeFileSync(eventPath, JSON.stringify(event, null, 2));

    const status = {
      updatedAt: new Date().toISOString(),
      lastEventId: event.id,
      lastMatched: event.matched,
      lastMessageType: event.messageType,
      lastSource: event.source,
      matchCount: event.matches.length,
      outbound: event.outbound ?? null,
    };
    ensureDir(dirname(latestStatus));
    writeFileSync(latestStatus, JSON.stringify(status, null, 2));

    this.processed.add(event.dedupeKey);
    this.persistProcessedIndex();

    appendFileSync(
      join(eventsDir, '..', 'events.jsonl'),
      `${JSON.stringify({ id: event.id, at: event.receivedAt, matched: event.matched, type: event.messageType, source: event.source })}\n`
    );
  }

  inboxPath(): string {
    return this.paths().inboxDir;
  }

  archiveFile(fromPath: string): void {
    const { archiveDir } = this.paths();
    ensureDir(archiveDir);
    const base = fromPath.split('/').pop() ?? randomUUID();
    renameSync(fromPath, join(archiveDir, `${Date.now()}-${base}`));
  }
}

export function buildEvent(
  source: string,
  parsed: SwiftListenerEvent['parsed'],
  format: SwiftListenerEvent['format'],
  messageType: string,
  matches: SwiftListenerEvent['matches'],
  canonical: SwiftListenerEvent['canonical'],
  raw: string,
  dedupeKey: string
): SwiftListenerEvent {
  return {
    id: randomUUID(),
    receivedAt: new Date().toISOString(),
    source,
    format,
    messageType,
    matched: matches.length > 0,
    matches,
    parsed,
    canonical,
    rawPreview: raw.slice(0, 2000),
    dedupeKey,
  };
}
