import fs from "fs";
import { execSync } from "child_process";
import type { IPty } from "node-pty";
import { AgentRunner } from "./AgentRunner";
import { isWindows, findGitBash, loadNodePty, loadClaudeExtraFlags, windowsPathOverride } from "./runtime";

export class ClaudeRunner extends AgentRunner {
  id: string;
  name: string;
  type: 'mock' | 'claude' | 'claude-docker' | 'claude-ssh' | 'aider' | 'aider-docker' | 'shell' = '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();
  }

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

    // Detect Git Bash on Windows
    const useCmd = isWindows();
    const shellPath = useCmd ? (process.env.COMSPEC || "cmd.exe") : findGitBash();
    if (!shellPath) {
      this.status = 'stopped';
      this.appendLog("\x1b[1;31m[System Error] Shell not found. Cannot spawn terminal.\x1b[0m\r\n");
      return;
    }
    const shellArgs = useCmd ? ["/c"] : ["-c"];

    try {
      this.ptyModule = loadNodePty();

      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");
      
      const targetCwd = (this.workspaceRoot && fs.existsSync(this.workspaceRoot)) 
        ? this.workspaceRoot 
        : process.env.ORIGINAL_CWD || process.cwd();

      const extraFlags = loadClaudeExtraFlags(resume);

      // Setup process environment with injected keys
      const customEnv = {
        ...process.env,
        TERM: "xterm-256color",
        COLORTERM: "truecolor",
      } as NodeJS.ProcessEnv;

      if (isWindows()) {
        const winPath = windowsPathOverride();
        for (const key of Object.keys(customEnv)) {
          if (key.toLowerCase() === "path") {
            delete customEnv[key];
          }
        }
        customEnv.Path = winPath;
        customEnv.PATH = winPath;
      }

      // Check if 'claude' command exists in PATH, fallback to npx if not
      let baseCmd = "claude";
      try {
        const checkCmd = isWindows() ? "where.exe claude" : "which claude";
        execSync(checkCmd, { env: customEnv, stdio: "ignore" });
      } catch {
        baseCmd = "npx -y @anthropic-ai/claude-code";
      }

      const claudeCmd = `${baseCmd} ${extraFlags}`.trim();
      this.appendLog(`[System] Spawning command: ${claudeCmd}\r\n`);

      // Spawn shell and run 'claude' command
      this.ptyProcess = this.ptyModule!.spawn(shellPath, [...shellArgs, claudeCmd], {
        name: "xterm-256color",
        cols: 120,
        rows: 30,
        cwd: targetCwd,
        env: customEnv,
      });

      // 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;
    }

    this.agentState = 'thinking';
    await this.writeToPty(this.ptyProcess, isRaw ? text : text + "\r");
  }

  override resize(cols: number, rows: number): void {
    if (this.ptyProcess) {
      try {
        this.ptyProcess.resize(cols, rows);
      } catch (err) {
        console.error("[ClaudeRunner] Failed to resize PTY:", err);
      }
    }
  }

  override getPid(): number | undefined {
    return this.ptyProcess?.pid;
  }
}

