export interface RunnerStatus {
  id: string;
  name: string;
  type: 'mock' | 'claude' | 'claude-docker' | 'claude-ssh' | 'aider' | 'aider-docker';
  status: 'idle' | 'running' | 'stopped';
  createdAt: string;
  logsCount: number;
  workspaceRoot?: string;
  scopedDirs?: string[];
  tokenUsage?: { input: number; output: number; cost: number };
}

export abstract class AgentRunner {
  abstract id: string;
  abstract name: string;
  abstract type: 'mock' | 'claude' | 'claude-docker' | 'claude-ssh' | 'aider' | 'aider-docker';
  abstract status: 'idle' | 'running' | 'stopped';
  abstract createdAt: string;
  abstract logs: string[];
  workspaceRoot?: string;
  scopedDirs?: string[];
  tokenUsage: { input: number; output: number; cost: number } = { input: 0, output: 0, cost: 0 };

  protected parseTokenUsage(data: string): void {
    const cleanData = data.replace(/\x1b\[[0-9;]*m/g, ""); // Strip ANSI colors

    // Regex for cost
    const costMatch = cleanData.match(/(?:cost|estimated cost)[:\s]*\$([0-9.]+)/i);
    if (costMatch) {
      this.tokenUsage.cost = parseFloat(costMatch[1]);
    }

    const parseNum = (str: string): number => {
      str = str.replace(/,/g, '').toLowerCase().trim();
      if (str.endsWith('k')) {
        return parseFloat(str.slice(0, -1)) * 1000;
      }
      if (str.endsWith('m')) {
        return parseFloat(str.slice(0, -1)) * 1000000;
      }
      return parseInt(str, 10) || 0;
    };

    // Tokens pattern: "Tokens: 1,500 sent, 300 received" or "1.5k sent, 0.3k received"
    const tokensPattern1 = /tokens?[:\s]*([0-9,.]+[km]?)\s*(?:sent|input|prompt)[^a-zA-Z0-9]+([0-9,.]+[km]?)\s*(?:received|output|completion)/i;
    const match1 = cleanData.match(tokensPattern1);
    if (match1) {
      this.tokenUsage.input = parseNum(match1[1]);
      this.tokenUsage.output = parseNum(match1[2]);
    } else {
      // Try separate matches
      const inputMatch = cleanData.match(/([0-9,.]+[km]?)\s*(?:sent|input|prompt)\s*tokens?/i) || cleanData.match(/tokens?\s*(?:sent|input|prompt)[:\s]*([0-9,.]+[km]?)/i);
      if (inputMatch) {
        this.tokenUsage.input = parseNum(inputMatch[1]);
      }
      const outputMatch = cleanData.match(/([0-9,.]+[km]?)\s*(?:received|output|completion)\s*tokens?/i) || cleanData.match(/tokens?\s*(?:received|output|completion)[:\s]*([0-9,.]+[km]?)/i);
      if (outputMatch) {
        this.tokenUsage.output = parseNum(outputMatch[1]);
      }
    }

    // Fallback cost estimation if input/output is updated but cost is 0/unmatched
    if (this.tokenUsage.cost === 0 && (this.tokenUsage.input > 0 || this.tokenUsage.output > 0)) {
      this.tokenUsage.cost = (this.tokenUsage.input * 0.000003) + (this.tokenUsage.output * 0.000015);
    }
  }

  abstract start(): Promise<void>;
  abstract stop(): Promise<void>;
  abstract sendInput(text: string, isRaw?: boolean): Promise<void>;
  abstract getStatus(): RunnerStatus;

  protected listeners: ((data: string) => void)[] = [];

  public onOutput(callback: (data: string) => void): void {
    this.listeners.push(callback);
  }

  public offOutput(callback: (data: string) => void): void {
    this.listeners = this.listeners.filter(cb => cb !== callback);
  }

  protected emitOutput(data: string): void {
    this.listeners.forEach(cb => cb(data));
  }
}
