import fs from "fs";
import path from "path";
import type { IPty } from "node-pty";
import { AgentRunner, RunnerStatus } from "./AgentRunner";
import { SessionPersister } from "./SessionPersister";

function isWindows(): boolean {
  const key = "platform";
  return (process as unknown as Record<string, unknown>)[key] === "win32";
}

function findGitBash(): string | null {
  if (!isWindows()) {
    return "bash";
  }
  const pf = process.env.PROGRAMFILES || "C:\\Program Files";
  const pf86 = process.env["PROGRAMFILES(X86)"] || "C:\\Program Files (x86)";
  const local = process.env.LOCALAPPDATA || "";

  const candidates: string[] = [
    process.env.WINGMAN_BASH_PATH,
    path.join(pf, "Git", "bin", "bash.exe"),
    path.join(pf86, "Git", "bin", "bash.exe"),
    local ? path.join(local, "Programs", "Git", "bin", "bash.exe") : "",
  ].filter((p): p is string => typeof p === "string" && p !== "");

  return candidates.find(p => fs.existsSync(p)) || null;
}

export class DockerRunner extends AgentRunner {
  id: string;
  name: string;
  type: 'mock' | 'claude' | 'claude-docker' | 'claude-ssh' | 'aider' | 'aider-docker' = 'claude-docker';
  status: 'idle' | 'running' | 'stopped' = 'idle';
  createdAt: string;
  logs: string[] = [];

  private ptyProcess: IPty | null = null;
  private ptyModule: typeof import("node-pty") | null = null;

  constructor(id: string, name?: string) {
    super();
    this.id = id;
    this.name = name || `Docker Session ${id.substring(0, 8)}`;
    this.createdAt = new Date().toISOString();
  }

  private appendLog(line: string) {
    this.logs.push(line);
    if (this.logs.length > 5000) {
      this.logs.shift();
    }
    this.parseTokenUsage(line);
    this.emitOutput(line);
    SessionPersister.saveSession(this);
  }

  async start(): Promise<void> {
    if (this.status === 'running') return;

    this.status = 'running';
    this.appendLog("\x1b[1;35m==================================================\x1b[0m\r\n");
    this.appendLog("\x1b[1;36m       NEURALLOOM DOCKER SANDBOX RUNTIME BRIDGE    \x1b[0m\r\n");
    this.appendLog("\x1b[1;35m==================================================\x1b[0m\r\n");
    this.appendLog("[System] Verifying Docker environment accessibility...\r\n");

    const bashPath = findGitBash();
    if (!bashPath) {
      this.status = 'stopped';
      this.appendLog("\x1b[1;31m[System Error] Git Bash not found. Cannot bootstrap PTY sandbox.\x1b[0m\r\n");
      return;
    }

    try {
      if (!this.ptyModule) {
        this.ptyModule = await import("node-pty");
      }

      const targetCwd = (this.workspaceRoot && fs.existsSync(this.workspaceRoot)) 
        ? this.workspaceRoot 
        : process.cwd();

      // Check if docker executable exists by spawning `docker -v`
      const testPty = this.ptyModule.spawn(bashPath, ["-c", "docker --version"], {
        name: "xterm-256color",
        cols: 80,
        rows: 10,
        cwd: targetCwd,
      });

      let hasDocker = false;
      testPty.onData((data) => {
        if (data.toLowerCase().includes("docker version")) {
          hasDocker = true;
        }
      });

      // Wait briefly for docker check
      await new Promise(resolve => setTimeout(resolve, 800));
      testPty.kill();

      if (!hasDocker) {
        this.status = 'stopped';
        this.appendLog("\x1b[1;31m[Dependency Warning] Docker command not recognized in local PATH.\x1b[0m\r\n");
        this.appendLog("\x1b[1;33mPlease install Docker Desktop (https://www.docker.com/products/docker-desktop) and ensure the daemon is running.\x1b[0m\r\n");
        this.appendLog("\x1b[1;31m[System] Sandbox launch aborted.\x1b[0m\r\n");
        return;
      }

      // If docker is available, spawn container with mounted workspace
      this.appendLog("[System] Docker detected. Initializing container mount...\r\n");
      
      const mounts: string[] = [`-v "${targetCwd}:/workspace"`];
      if (this.scopedDirs && Array.isArray(this.scopedDirs)) {
        this.scopedDirs.forEach((dir) => {
          if (fs.existsSync(dir)) {
            const folderName = path.basename(dir).replace(/[^a-zA-Z0-9-_]/g, "_");
            mounts.push(`-v "${dir}:/scoped/${folderName}"`);
          }
        });
      }
      const mountsStr = mounts.join(" ");

      const dockerCmd = `docker run -it --name neural-loom-session-${this.id} --rm ${mountsStr} -w /workspace node:20-alpine npx -y @anthropic-ai/claude-code --agents-md`;

      this.ptyProcess = this.ptyModule.spawn(bashPath, ["-c", dockerCmd], {
        name: "xterm-256color",
        cols: 120,
        rows: 30,
        cwd: targetCwd,
        env: {
          ...process.env,
          TERM: "xterm-256color",
          COLORTERM: "truecolor",
        },
      });

      this.ptyProcess.onData((data: string) => {
        this.appendLog(data);
      });

      this.ptyProcess.onExit(({ exitCode, signal }: { exitCode: number; signal?: number }) => {
        this.status = 'stopped';
        this.appendLog(`\r\n\x1b[1;31m[System] Docker Sandbox exited (code=${exitCode}, signal=${signal}).\x1b[0m\r\n`);
        this.ptyProcess = null;
      });

    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : "Unknown error";
      this.status = 'stopped';
      this.appendLog(`\x1b[1;31m[System Error] Failed to launch Docker runner: ${errorMessage}\x1b[0m\r\n`);
    }
  }

  async stop(): Promise<void> {
    this.status = 'stopped';
    if (this.ptyProcess) {
      try {
        this.ptyProcess.kill();
      } catch (err) {
        console.error("Error killing Docker PTY process:", err);
      }
      this.ptyProcess = null;
    }
    this.appendLog("\r\n\x1b[1;31m[System] Docker container stopped and unmounted.\x1b[0m\r\n");
  }

  async sendInput(text: string, isRaw = false): Promise<void> {
    if (this.status !== 'running' || !this.ptyProcess) {
      this.appendLog(`\r\n\x1b[1;31m[Error] Cannot write: Docker container is ${this.status}.\x1b[0m\r\n`);
      return;
    }

    this.ptyProcess.write(isRaw ? text : text + "\r");
  }

  getStatus(): RunnerStatus {
    return {
      id: this.id,
      name: this.name,
      type: this.type,
      status: this.status,
      createdAt: this.createdAt,
      logsCount: this.logs.length,
      workspaceRoot: this.workspaceRoot,
      scopedDirs: this.scopedDirs,
      tokenUsage: this.tokenUsage
    };
  }
}
