import crypto from "crypto";
import { AgentRunner, RunnerStatus } from "./AgentRunner";
import { MockRunner } from "./MockRunner";
import { ClaudeRunner } from "./ClaudeRunner";
import { DockerRunner } from "./DockerRunner";
import { SSHRunner } from "./SSHRunner";
import { AiderRunner } from "./AiderRunner";
import { initWebSocketServer } from "./WebSocketServer";
import { ContextManager } from "./ContextManager";
import { SessionPersister } from "./SessionPersister";

export class SessionManager {
  private static instance: SessionManager | null = null;
  private sessions = new Map<string, AgentRunner>();

  private constructor() {
    // Bootstrap staging filesystem and slash commands
    ContextManager.initializeStaging();
    ContextManager.compileGlobalRules();

    // Reload persisted sessions
    try {
      const saved = SessionPersister.loadAllSessions();
      for (const data of saved) {
        let runner: AgentRunner;
        if (data.type === "mock") {
          runner = new MockRunner(data.id, data.name);
        } else if (data.type === "claude-docker") {
          runner = new DockerRunner(data.id, data.name);
        } else if (data.type === "claude-ssh") {
          runner = new SSHRunner(data.id, data.name);
        } else if (data.type === "aider" || data.type === "aider-docker") {
          runner = new AiderRunner(data.id, data.type as 'aider' | 'aider-docker', data.name);
        } else {
          runner = new ClaudeRunner(data.id, data.name);
        }

        runner.status = data.status;
        runner.createdAt = data.createdAt;
        runner.logs = data.logs;
        runner.workspaceRoot = data.workspaceRoot;
        runner.scopedDirs = data.scopedDirs;

        // Auto-resume mock runner if running
        if (data.type === "mock" && data.status === "running") {
          runner.start();
        }

        this.sessions.set(data.id, runner);
      }
    } catch (err) {
      console.error("[SessionManager] Failed to reload saved sessions:", err);
    }
  }

  public static getInstance(): SessionManager {
    if (!SessionManager.instance) {
      SessionManager.instance = new SessionManager();
      // Initialize WebSocket gateway server on port 3001
      initWebSocketServer(3001);
    }
    return SessionManager.instance;
  }

  private generateId(): string {
    return crypto.randomUUID();
  }

  public async createSession(
    type: 'mock' | 'claude' | 'claude-docker' | 'claude-ssh' | 'aider' | 'aider-docker',
    name?: string,
    workspaceRoot?: string,
    scopedDirs?: string[]
  ): Promise<AgentRunner> {
    const id = this.generateId();
    let runner: AgentRunner;

    if (type === 'mock') {
      runner = new MockRunner(id, name);
    } else if (type === 'claude-docker') {
      runner = new DockerRunner(id, name);
    } else if (type === 'claude-ssh') {
      runner = new SSHRunner(id, name);
    } else if (type === 'aider' || type === 'aider-docker') {
      runner = new AiderRunner(id, type, name);
    } else {
      runner = new ClaudeRunner(id, name);
    }

    runner.workspaceRoot = workspaceRoot;
    runner.scopedDirs = scopedDirs;

    await runner.start();
    this.sessions.set(id, runner);
    SessionPersister.saveSession(runner);
    return runner;
  }

  public getSession(id: string): AgentRunner | undefined {
    return this.sessions.get(id);
  }

  public listSessions(): RunnerStatus[] {
    return Array.from(this.sessions.values()).map(runner => runner.getStatus());
  }

  public async stopSession(id: string): Promise<boolean> {
    const runner = this.sessions.get(id);
    if (!runner) return false;
    await runner.stop();
    SessionPersister.saveSession(runner);
    return true;
  }

  public async deleteSession(id: string): Promise<boolean> {
    const runner = this.sessions.get(id);
    if (runner) {
      await runner.stop();
    }
    SessionPersister.deleteSession(id);
    return this.sessions.delete(id);
  }

  public async injectCommand(id: string, command: string): Promise<boolean> {
    const runner = this.sessions.get(id);
    if (!runner || runner.status !== 'running') return false;

    const root = runner.workspaceRoot || process.cwd();

    if (command === '/ccc' || command === '/ccp') {
      ContextManager.compileSessionContext(root, runner.scopedDirs);
    }

    let finalCommand = command;
    if (runner.type === 'aider' || runner.type === 'aider-docker') {
      if (command === '/ccc') {
        finalCommand = "/read .ai/neural-loom/ccontext.md";
      } else if (command === '/ccp') {
        finalCommand = "/read .ai/neural-loom/cprompt.md";
      }
    }

    // Inject Ctrl+U to clear input, then command, then Enter (\r)
    await runner.sendInput("\x15" + finalCommand + "\r", true);
    return true;
  }
}
