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 ClaudeRunner extends AgentRunner {
  id: string;
  name: string;
  type: 'mock' | 'claude' = 'claude';
  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 || `Claude 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;

    // Detect Git Bash on Windows
    const bashPath = findGitBash();
    if (!bashPath) {
      this.status = 'stopped';
      this.appendLog("\x1b[1;31m[System Error] Git Bash not found. Please install Git for Windows.\x1b[0m\r\n");
      return;
    }

    try {
      // Dynamic import to prevent bundler SSR/Turbopack errors on server start
      if (!this.ptyModule) {
        this.ptyModule = await import("node-pty");
      }

      this.status = 'running';
      this.appendLog("\x1b[1;35m==================================================\x1b[0m\r\n");
      this.appendLog("\x1b[1;36m       NEURALLOOM ACTIVE CLAUDE TERMINAL ROUTE     \x1b[0m\r\n");
      this.appendLog("\x1b[1;35m==================================================\x1b[0m\r\n");
      this.appendLog(`[System] Spawning Claude Code CLI process using Git Bash...\r\n`);

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

      // Spawn bash and run 'claude' command
      this.ptyProcess = this.ptyModule.spawn(bashPath, ["-c", "claude --agents-md"], {
        name: "xterm-256color",
        cols: 120,
        rows: 30,
        cwd: targetCwd,
        env: {
          ...process.env,
          TERM: "xterm-256color",
          COLORTERM: "truecolor",
          // Inject custom path just in case child processes need Visual Studio Node on Windows
          ...(isWindows() ? {
            PATH: "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Microsoft\\VisualStudio\\NodeJs;" + process.env.PATH
          } : {})
        },
      });

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

      // Capture process exit
      this.ptyProcess.onExit(({ exitCode, signal }: { exitCode: number; signal?: number }) => {
        this.status = 'stopped';
        this.appendLog(`\r\n\x1b[1;31m[System] Claude Code PTY process 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 initialize node-pty runner: ${errorMessage}\x1b[0m\r\n`);
      console.error("Failed to start Claude PTY runner:", err);
    }
  }

  async stop(): Promise<void> {
    this.status = 'stopped';
    if (this.ptyProcess) {
      try {
        this.ptyProcess.kill();
      } catch (err) {
        console.error("Error killing PTY process:", err);
      }
      this.ptyProcess = null;
    }
    this.appendLog("\r\n\x1b[1;31m[System] Terminal bridge terminated.\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: session process is ${this.status}.\x1b[0m\r\n`);
      return;
    }

    // PTY processes require carriage return (\r) to execute the command line
    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
    };
  }
}
