import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
import { firstValueFrom } from 'rxjs';
import { TaskRepository } from '../../infrastructure/repositories/task.repository';
import { RunbookRepository } from '../../infrastructure/repositories/runbook.repository';
import { OpsTask } from '../../domain/entities/ops-task.entity';
import * as crypto from 'crypto';

@Injectable()
export class ExecuteRunbookUseCase {
  private readonly logger = new Logger(ExecuteRunbookUseCase.name);
  private readonly agentServiceUrl: string;

  constructor(
    private readonly taskRepository: TaskRepository,
    private readonly runbookRepository: RunbookRepository,
    private readonly httpService: HttpService,
    private readonly configService: ConfigService,
  ) {
    this.agentServiceUrl = this.configService.get<string>(
      'AGENT_SERVICE_URL',
      'http://localhost:3001',
    );
  }

  async execute(
    runbookId: string,
    targetServerIds: string[],
    triggerContext?: Record<string, unknown>,
  ): Promise<OpsTask> {
    const runbook = await this.runbookRepository.findById(runbookId);
    if (!runbook) {
      throw new NotFoundException(`Runbook ${runbookId} not found`);
    }

    if (!runbook.isActive) {
      throw new Error(`Runbook ${runbookId} is not active`);
    }

    this.logger.log(`Executing runbook "${runbook.name}" on ${targetServerIds.length} servers`);

    const task = new OpsTask();
    task.id = crypto.randomUUID();
    task.tenantId = runbook.tenantId;
    task.type = runbook.triggerType === 'alert' ? 'recovery' : 'inspection';
    task.title = `Runbook: ${runbook.name}`;
    task.description = runbook.description;
    task.status = 'running';
    task.priority = runbook.maxRiskLevel;
    task.targetServers = targetServerIds;
    task.runbookId = runbookId;
    task.createdBy = 'system:runbook';
    task.createdAt = new Date();

    await this.taskRepository.save(task);

    try {
      const response = await firstValueFrom(
        this.httpService.post(`${this.agentServiceUrl}/api/v1/agent/tasks`, {
          tenantId: runbook.tenantId,
          instructions: runbook.promptTemplate,
          skills: runbook.allowedTools,
          runbookId,
          maxRiskLevel: runbook.maxRiskLevel,
          targets: { serverIds: targetServerIds },
          triggerContext,
        }),
      );

      task.agentSessionId = response.data.sessionId;
      task.status = response.data.status === 'success' ? 'completed' : 'failed';
      task.resultSummary = response.data.summary;
      task.completedAt = new Date();
    } catch (error: any) {
      task.status = 'failed';
      task.resultSummary = `Execution error: ${error.message}`;
      task.completedAt = new Date();
      this.logger.error(`Runbook execution failed: ${error.message}`);
    }

    return this.taskRepository.save(task);
  }

  async dryRun(
    runbookId: string,
    targetServerIds: string[],
  ): Promise<{ steps: string[]; estimatedDuration: number; riskLevel: number; warnings: string[] }> {
    const runbook = await this.runbookRepository.findById(runbookId);
    if (!runbook) {
      throw new NotFoundException(`Runbook ${runbookId} not found`);
    }

    return {
      steps: [`Execute prompt template on ${targetServerIds.length} servers`],
      estimatedDuration: targetServerIds.length * 60,
      riskLevel: runbook.maxRiskLevel,
      warnings: runbook.autoApprove ? ['Auto-approve is enabled for this runbook'] : [],
    };
  }
}
