/**
 * Automation Pipeline Manager
 * Handles task execution, scheduling, and monitoring for CMS automation
 */

import {
  AutomationTask,
  AutomationPipeline,
  AutomationEvent,
  ExecutionResult,
  ScheduleConfig,
} from "./types.js";

export class AutomationPipelineManager {
  private pipelines: Map<string, AutomationPipeline> = new Map();
  private runningTasks: Map<string, Promise<ExecutionResult>> = new Map();
  private eventHandlers: Map<string, ((event: AutomationEvent) => void)[]> =
    new Map();
  private nextTaskId = 1;
  private nextPipelineId = 1;

  /**
   * Create a new automation task
   */
  createTask(
    taskData: Omit<AutomationTask, "id" | "createdAt" | "status">
  ): AutomationTask {
    const task: AutomationTask = {
      id: `task_${this.nextTaskId++}`,
      createdAt: new Date(),
      status: "pending",
      ...taskData,
    };

    this.emitEvent({
      type: "task_created",
      taskId: task.id,
      timestamp: new Date(),
      data: { task },
    });

    return task;
  }

  /**
   * Create a new automation pipeline
   */
  createPipeline(
    pipelineData: Omit<AutomationPipeline, "id" | "createdAt" | "status">
  ): AutomationPipeline {
    const pipeline: AutomationPipeline = {
      id: `pipeline_${this.nextPipelineId++}`,
      createdAt: new Date(),
      status: "pending",
      ...pipelineData,
    };

    this.pipelines.set(pipeline.id, pipeline);

    this.emitEvent({
      type: "pipeline_created",
      pipelineId: pipeline.id,
      timestamp: new Date(),
      data: { pipeline },
    });

    return pipeline;
  }

  /**
   * Execute a single automation task
   */
  async executeTask(task: AutomationTask): Promise<ExecutionResult> {
    const startTime = Date.now();

    this.emitEvent({
      type: "task_started",
      taskId: task.id,
      timestamp: new Date(),
      data: { task },
    });

    try {
      // Simulate task execution based on type
      const result = await this.performTaskExecution(task);

      const executionTime = Date.now() - startTime;

      const finalResult: ExecutionResult = {
        ...result,
        taskId: task.id,
        executionTime,
        timestamp: new Date(),
      };

      this.emitEvent({
        type: result.success ? "task_completed" : "task_failed",
        taskId: task.id,
        timestamp: new Date(),
        data: { result: finalResult },
      });

      return finalResult;
    } catch (error) {
      const executionTime = Date.now() - startTime;
      const errorResult: ExecutionResult = {
        taskId: task.id,
        success: false,
        error: error instanceof Error ? error.message : "Unknown error",
        executionTime,
        timestamp: new Date(),
      };

      this.emitEvent({
        type: "task_failed",
        taskId: task.id,
        timestamp: new Date(),
        data: { error: errorResult.error },
      });

      return errorResult;
    }
  }

  /**
   * Execute an automation pipeline
   */
  async executePipeline(pipelineId: string): Promise<{
    success: boolean;
    results: ExecutionResult[];
    failedTasks: string[];
    skippedTasks: string[];
  }> {
    const pipeline = this.pipelines.get(pipelineId);
    if (!pipeline) {
      throw new Error(`Pipeline not found: ${pipelineId}`);
    }

    this.emitEvent({
      type: "pipeline_started",
      pipelineId,
      timestamp: new Date(),
      data: { pipeline },
    });

    const results: ExecutionResult[] = [];
    const failedTasks: string[] = [];
    const skippedTasks: string[] = [];

    try {
      if (pipeline.executionMode === "parallel") {
        // Execute all tasks in parallel
        const taskPromises = pipeline.tasks.map((task) =>
          this.executeTask(task)
        );
        const taskResults = await Promise.allSettled(taskPromises);

        taskResults.forEach((result, index) => {
          if (result.status === "fulfilled") {
            results.push(result.value);
            if (!result.value.success) {
              failedTasks.push(pipeline.tasks[index].id);
            }
          } else {
            const task = pipeline.tasks[index];
            failedTasks.push(task.id);
            results.push({
              taskId: task.id,
              success: false,
              error: result.reason?.message || "Task execution failed",
              executionTime: 0,
              timestamp: new Date(),
            });
          }
        });
      } else {
        // Execute tasks sequentially
        for (const task of pipeline.tasks) {
          // Check if we should skip this task due to previous failures
          if (
            pipeline.failureStrategy === "stop_on_failure" &&
            failedTasks.length > 0
          ) {
            skippedTasks.push(task.id);
            continue;
          }

          const result = await this.executeTask(task);
          results.push(result);

          if (!result.success) {
            failedTasks.push(task.id);

            if (pipeline.failureStrategy === "stop_on_failure") {
              // Skip remaining tasks
              const remainingTasks = pipeline.tasks.slice(
                pipeline.tasks.indexOf(task) + 1
              );
              skippedTasks.push(...remainingTasks.map((t) => t.id));
              break;
            }
          }
        }
      }

      const success = failedTasks.length === 0;

      this.emitEvent({
        type: success ? "pipeline_completed" : "pipeline_failed",
        pipelineId,
        timestamp: new Date(),
        data: {
          success,
          totalTasks: pipeline.tasks.length,
          failedTasks: failedTasks.length,
          skippedTasks: skippedTasks.length,
        },
      });

      return {
        success,
        results,
        failedTasks,
        skippedTasks,
      };
    } catch (error) {
      this.emitEvent({
        type: "pipeline_failed",
        pipelineId,
        timestamp: new Date(),
        data: {
          error: error instanceof Error ? error.message : "Unknown error",
        },
      });

      throw error;
    }
  }

  /**
   * Retry a failed task with exponential backoff
   */
  async retryTask(
    task: AutomationTask,
    maxRetries: number = 3,
    backoffMs: number = 1000
  ): Promise<ExecutionResult> {
    let lastError: string = "";

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      this.emitEvent({
        type: "task_retry",
        taskId: task.id,
        timestamp: new Date(),
        data: { attempt, maxRetries },
      });

      const result = await this.executeTask(task);

      if (result.success) {
        return result;
      }

      lastError = result.error || "Unknown error";

      if (attempt < maxRetries) {
        const delay = backoffMs * Math.pow(2, attempt - 1);
        await new Promise((resolve) => setTimeout(resolve, delay));
      }
    }

    return {
      taskId: task.id,
      success: false,
      error: `Failed after ${maxRetries} attempts. Last error: ${lastError}`,
      executionTime: 0,
      timestamp: new Date(),
    };
  }

  /**
   * Schedule a pipeline for execution
   */
  schedulePipeline(pipelineId: string, schedule: ScheduleConfig): void {
    const pipeline = this.pipelines.get(pipelineId);
    if (!pipeline) {
      throw new Error(`Pipeline not found: ${pipelineId}`);
    }

    // In a real implementation, this would integrate with a job scheduler
    // For now, we'll just emit an event
    this.emitEvent({
      type: "pipeline_scheduled",
      pipelineId,
      timestamp: new Date(),
      data: { schedule },
    });
  }

  /**
   * Add event listener
   */
  addEventListener(
    eventType: string,
    handler: (event: AutomationEvent) => void
  ): void {
    if (!this.eventHandlers.has(eventType)) {
      this.eventHandlers.set(eventType, []);
    }
    this.eventHandlers.get(eventType)!.push(handler);
  }

  /**
   * Remove event listener
   */
  removeEventListener(
    eventType: string,
    handler: (event: AutomationEvent) => void
  ): void {
    const handlers = this.eventHandlers.get(eventType);
    if (handlers) {
      const index = handlers.indexOf(handler);
      if (index > -1) {
        handlers.splice(index, 1);
      }
    }
  }

  /**
   * Get pipeline status
   */
  getPipelineStatus(pipelineId: string): AutomationPipeline | undefined {
    return this.pipelines.get(pipelineId);
  }

  /**
   * Get all pipelines
   */
  getAllPipelines(): AutomationPipeline[] {
    return Array.from(this.pipelines.values());
  }

  /**
   * Cancel a running pipeline
   */
  async cancelPipeline(pipelineId: string): Promise<boolean> {
    const pipeline = this.pipelines.get(pipelineId);
    if (!pipeline) {
      return false;
    }

    if (pipeline.status === "running") {
      pipeline.status = "cancelled";

      this.emitEvent({
        type: "pipeline_cancelled",
        pipelineId,
        timestamp: new Date(),
        data: { pipeline },
      });

      return true;
    }

    return false;
  }

  /**
   * Rollback a pipeline execution
   */
  async rollbackPipeline(pipelineId: string): Promise<{
    success: boolean;
    rollbackResults: ExecutionResult[];
  }> {
    const pipeline = this.pipelines.get(pipelineId);
    if (!pipeline) {
      throw new Error(`Pipeline not found: ${pipelineId}`);
    }

    this.emitEvent({
      type: "pipeline_rollback_started",
      pipelineId,
      timestamp: new Date(),
      data: { pipeline },
    });

    const rollbackResults: ExecutionResult[] = [];

    // Execute rollback tasks in reverse order
    for (const task of [...pipeline.tasks].reverse()) {
      if (task.rollbackConfiguration) {
        const rollbackTask: AutomationTask = {
          ...task,
          id: `rollback_${task.id}`,
          type: "rollback",
          configuration: task.rollbackConfiguration,
        };

        const result = await this.executeTask(rollbackTask);
        rollbackResults.push(result);
      }
    }

    const success = rollbackResults.every((result) => result.success);

    this.emitEvent({
      type: success
        ? "pipeline_rollback_completed"
        : "pipeline_rollback_failed",
      pipelineId,
      timestamp: new Date(),
      data: { success, results: rollbackResults },
    });

    return {
      success,
      rollbackResults,
    };
  }

  /**
   * Get system health status
   */
  getSystemHealth(): {
    status: "healthy" | "warning" | "critical";
    activePipelines: number;
    runningTasks: number;
    failedTasksLast24h: number;
    systemLoad: number;
  } {
    const activePipelines = Array.from(this.pipelines.values()).filter(
      (p) => p.status === "running"
    ).length;

    const runningTasks = this.runningTasks.size;

    // Simplified health calculation
    let status: "healthy" | "warning" | "critical" = "healthy";

    if (runningTasks > 10) {
      status = "warning";
    }

    if (runningTasks > 20) {
      status = "critical";
    }

    return {
      status,
      activePipelines,
      runningTasks,
      failedTasksLast24h: 0, // Would track this in a real implementation
      systemLoad: Math.random() * 100, // Simulated system load
    };
  }

  /**
   * Perform the actual task execution based on task type
   */
  private async performTaskExecution(
    task: AutomationTask
  ): Promise<Omit<ExecutionResult, "taskId" | "executionTime" | "timestamp">> {
    // Simulate execution time
    const executionTime = Math.random() * 2000 + 500;
    await new Promise((resolve) => setTimeout(resolve, executionTime));

    // Simulate success/failure based on task type
    const successRate = this.getTaskSuccessRate(task.type);
    const success = Math.random() < successRate;

    if (success) {
      return {
        success: true,
        message: `${task.type} task completed successfully`,
        output: this.generateTaskOutput(task),
      };
    } else {
      return {
        success: false,
        error: `${task.type} task failed: Simulated failure`,
      };
    }
  }

  /**
   * Get success rate for different task types
   */
  private getTaskSuccessRate(taskType: string): number {
    const rates: Record<string, number> = {
      validation: 0.95,
      deployment: 0.85,
      backup: 0.98,
      migration: 0.8,
      test: 0.9,
      cleanup: 0.95,
      monitoring: 0.99,
      rollback: 0.85,
    };

    return rates[taskType] || 0.85;
  }

  /**
   * Generate sample output for tasks
   */
  private generateTaskOutput(task: AutomationTask): Record<string, unknown> {
    const baseOutput = {
      taskId: task.id,
      taskType: task.type,
      environment: task.environment,
      completedAt: new Date().toISOString(),
    };

    switch (task.type) {
      case "deployment":
        return {
          ...baseOutput,
          deployedVersion: "1.2.3",
          deploymentUrl: "https://example.com",
          healthChecksPassed: true,
        };

      case "backup":
        return {
          ...baseOutput,
          backupLocation: "/backups/cms_backup_" + Date.now(),
          backupSize: "2.3GB",
          compressionRatio: 0.65,
        };

      case "test":
        return {
          ...baseOutput,
          testsRun: 142,
          testsPassed: 140,
          testsFailed: 2,
          coverage: 0.94,
        };

      default:
        return baseOutput;
    }
  }

  /**
   * Emit an automation event
   */
  private emitEvent(event: AutomationEvent): void {
    const handlers = this.eventHandlers.get(event.type);
    if (handlers) {
      handlers.forEach((handler) => {
        try {
          handler(event);
        } catch (error) {
          console.error("Error in event handler:", error);
        }
      });
    }
  }
}

// Export a default instance
export const automationPipeline = new AutomationPipelineManager();
