/*
 * Priority Forge — memory-hub sync-reader
 *
 * Periodically polls the active MemoryHub for cross-forge events, maintains
 * an in-memory cache and an ownership map (taskId → agentId), and runs
 * onAfterPoll callbacks (the integration uses these to re-derive blockers).
 *
 * Lifecycle: start() / stop() are idempotent. Instantiated by
 * createMemoryHubIntegration() in integration.ts — no module-level singleton.
 */

import { loadMemoryHubConfig } from './config';
import { getMemoryHub } from './factory';
import { TeamMemoryEvent } from './types';

interface ReaderState {
  teamId: string;
  agentId: string;
  lastSeenTs?: string;
  cache: Map<string, TeamMemoryEvent>;     // eventId → event
  ownership: Map<string, string>;          // taskId → agentId (last writer wins)
  taskStatus: Map<string, string>;         // taskId → latest known status
}

const CACHE_CAP = 1000;

export class TeamMemorySyncReader {
  private state: ReaderState | null = null;
  private timer?: ReturnType<typeof setInterval>;
  private polling = false;
  private onPollCallbacks: Array<() => void | Promise<void>> = [];

  isRunning(): boolean {
    return !!this.timer;
  }

  start(): void {
    if (this.timer) return;
    const cfg = loadMemoryHubConfig();
    if (!cfg.enabled || !cfg.teamId || !cfg.agentId) return;
    if (cfg.backend === 'noop') return;
    this.state = {
      teamId: cfg.teamId,
      agentId: cfg.agentId,
      cache: new Map(),
      ownership: new Map(),
      taskStatus: new Map(),
    };
    const interval = cfg.syncIntervalMs > 0 ? cfg.syncIntervalMs : 60000;
    this.timer = setInterval(() => {
      void this.poll();
    }, interval);
    void this.poll();
    console.log(`[memory-hub:reader] started (teamId=${cfg.teamId}, interval=${interval}ms)`);
  }

  stop(): void {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = undefined;
    }
    this.state = null;
    console.log('[memory-hub:reader] stopped');
  }

  onAfterPoll(cb: () => void | Promise<void>): void {
    this.onPollCallbacks.push(cb);
  }

  async poll(): Promise<void> {
    if (!this.state || this.polling) return;
    this.polling = true;
    let learnedSomething = false;
    try {
      const hub = getMemoryHub();
      const events = await hub.read({
        teamId: this.state.teamId,
        sinceTs: this.state.lastSeenTs,
        limit: 200,
      });
      // hub.read returns desc; iterate ascending so ownership "last writer wins"
      // reflects true chronological order.
      const asc = [...events].reverse();
      for (const ev of asc) {
        if (this.state.cache.has(ev.eventId)) continue;
        this.state.cache.set(ev.eventId, ev);
        const taskId = (ev.payload as { taskId?: string }).taskId;
        if (taskId) {
          if (
            ev.eventType === 'task_created' ||
            ev.eventType === 'task_selected' ||
            ev.eventType === 'task_updated'
          ) {
            this.state.ownership.set(taskId, ev.agentId);
          }
          const status = (ev.payload as { status?: string }).status;
          if (status) this.state.taskStatus.set(taskId, status);
          if (ev.eventType === 'task_completed') this.state.taskStatus.set(taskId, 'complete');
        }
        if (!this.state.lastSeenTs || ev.timestamp > this.state.lastSeenTs) {
          this.state.lastSeenTs = ev.timestamp;
        }
        learnedSomething = true;
      }
      if (this.state.cache.size > CACHE_CAP) {
        const entries = [...this.state.cache.entries()].sort((a, b) =>
          a[1].timestamp.localeCompare(b[1].timestamp),
        );
        const overflow = entries.length - CACHE_CAP;
        for (let i = 0; i < overflow; i += 1) this.state.cache.delete(entries[i][0]);
      }
    } catch (err) {
      console.error('[memory-hub:reader] poll failed:', err instanceof Error ? err.message : err);
    } finally {
      this.polling = false;
    }
    if (learnedSomething) {
      for (const cb of this.onPollCallbacks) {
        try {
          await cb();
        } catch (err) {
          console.error('[memory-hub:reader] post-poll callback failed:', err);
        }
      }
    }
  }

  getCachedEvents(): TeamMemoryEvent[] {
    if (!this.state) return [];
    return [...this.state.cache.values()].sort((a, b) => b.timestamp.localeCompare(a.timestamp));
  }

  getOwnershipMap(): Map<string, string> {
    return new Map(this.state?.ownership ?? []);
  }

  getOwnership(taskId: string): string | undefined {
    return this.state?.ownership.get(taskId);
  }

  getRemoteTaskStatus(taskId: string): string | undefined {
    return this.state?.taskStatus.get(taskId);
  }

  getDebugSnapshot(): {
    running: boolean;
    teamId: string | null;
    agentId: string | null;
    lastSeenTs: string | null;
    cacheSize: number;
    ownershipSize: number;
    statusSize: number;
  } {
    return {
      running: this.isRunning(),
      teamId: this.state?.teamId ?? null,
      agentId: this.state?.agentId ?? null,
      lastSeenTs: this.state?.lastSeenTs ?? null,
      cacheSize: this.state?.cache.size ?? 0,
      ownershipSize: this.state?.ownership.size ?? 0,
      statusSize: this.state?.taskStatus.size ?? 0,
    };
  }
}
