import { Injectable, Logger } from '@nestjs/common';
import { CommandGuardService } from '../../guards/command-guard.service';
import { CommandRiskLevel } from '../../../domain/value-objects/command-risk-level.vo';
import * as child_process from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import { promisify } from 'util';
import { glob } from 'glob';

const exec = promisify(child_process.exec);

export interface ToolExecutionResult {
  output: string;
  isError: boolean;
}

@Injectable()
export class ToolExecutor {
  private readonly logger = new Logger(ToolExecutor.name);

  constructor(private readonly commandGuard: CommandGuardService) {}

  async execute(
    toolName: string,
    input: Record<string, any>,
  ): Promise<ToolExecutionResult> {
    try {
      switch (toolName) {
        case 'Bash':
          return this.executeBash(input.command);
        case 'Read':
          return this.executeRead(input.file_path);
        case 'Write':
          return this.executeWrite(input.file_path, input.content);
        case 'Glob':
          return this.executeGlob(input.pattern, input.path);
        case 'Grep':
          return this.executeGrep(input.pattern, input.path, input.include);
        default:
          return { output: `Unknown tool: ${toolName}`, isError: true };
      }
    } catch (err: any) {
      this.logger.error(`Tool ${toolName} execution failed: ${err.message}`);
      return { output: `Error executing ${toolName}: ${err.message}`, isError: true };
    }
  }

  private async executeBash(command: string): Promise<ToolExecutionResult> {
    if (!command) {
      return { output: 'No command provided', isError: true };
    }

    // Classify command risk using CommandGuardService
    const riskLevel = this.commandGuard.classifyCommand(command);

    if (riskLevel >= CommandRiskLevel.HIGH_RISK) {
      const reason = this.commandGuard.getBlockReason(command);
      return {
        output: `Command blocked: risk level ${riskLevel} - ${reason ?? 'High-risk command'}`,
        isError: true,
      };
    }

    try {
      const { stdout, stderr } = await exec(command, {
        timeout: 30000,
        maxBuffer: 1024 * 1024, // 1MB buffer
      });
      return { output: stdout || stderr || '(no output)', isError: false };
    } catch (err: any) {
      // exec errors include stderr in the error object
      const output = err.stderr || err.stdout || err.message;
      return { output, isError: true };
    }
  }

  private async executeRead(filePath: string): Promise<ToolExecutionResult> {
    if (!filePath) {
      return { output: 'No file path provided', isError: true };
    }

    if (!fs.existsSync(filePath)) {
      return { output: `File not found: ${filePath}`, isError: true };
    }

    const content = fs.readFileSync(filePath, 'utf-8');
    return { output: content, isError: false };
  }

  private async executeWrite(
    filePath: string,
    content: string,
  ): Promise<ToolExecutionResult> {
    if (!filePath) {
      return { output: 'No file path provided', isError: true };
    }
    if (content === undefined || content === null) {
      return { output: 'No content provided', isError: true };
    }

    // Ensure parent directory exists
    const dir = path.dirname(filePath);
    if (!fs.existsSync(dir)) {
      fs.mkdirSync(dir, { recursive: true });
    }

    fs.writeFileSync(filePath, content, 'utf-8');
    return { output: `File written: ${filePath}`, isError: false };
  }

  private async executeGlob(
    pattern: string,
    searchPath?: string,
  ): Promise<ToolExecutionResult> {
    if (!pattern) {
      return { output: 'No pattern provided', isError: true };
    }

    const cwd = searchPath || process.cwd();
    const matches = await glob(pattern, { cwd, absolute: true });
    return { output: matches.join('\n') || '(no matches)', isError: false };
  }

  private async executeGrep(
    pattern: string,
    searchPath?: string,
    include?: string,
  ): Promise<ToolExecutionResult> {
    if (!pattern) {
      return { output: 'No pattern provided', isError: true };
    }

    const target = searchPath || '.';
    let command = `grep -rn "${pattern.replace(/"/g, '\\"')}" ${target}`;
    if (include) {
      command += ` --include="${include}"`;
    }
    // Limit output to prevent overwhelming results
    command += ' | head -100';

    try {
      const { stdout } = await exec(command, { timeout: 15000 });
      return { output: stdout || '(no matches)', isError: false };
    } catch {
      // grep returns exit code 1 when no matches found
      return { output: '(no matches)', isError: false };
    }
  }
}
