import * as fs from 'fs';
import * as path from 'path';
import { EventEmitter } from 'events';

/**
 * Workflow Engine
 * Manages multi-agent task execution with state persistence
 */

interface WorkflowStep {
  id: string;
  agent: string;
  task: string;
  dependsOn?: string[];
  status: 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
  output?: string;
  error?: string;
  startedAt?: Date;
  completedAt?: Date;
}

interface Workflow {
  id: string;
  name: string;
  description: string;
  steps: WorkflowStep[];
  context: Record<string, any>;
  status: 'pending' | 'running' | 'completed' | 'failed';
  createdAt: Date;
  updatedAt: Date;
}

interface WorkflowDefinition {
  name: string;
  description: string;
  steps: Array<{
    agent: string;
    task: string;
    dependsOn?: string[];
  }>;
}

export class WorkflowEngine extends EventEmitter {
  private workflowsDir: string;
  private activeWorkflows: Map<string, Workflow>;

  constructor() {
    super();
    this.workflowsDir = path.join(process.cwd(), '.opencode', 'workflows', 'state');
    this.activeWorkflows = new Map();
    this.ensureDirectories();
  }

  private ensureDirectories(): void {
    if (!fs.existsSync(this.workflowsDir)) {
      fs.mkdirSync(this.workflowsDir, { recursive: true });
    }
  }

  /**
   * Create a new workflow
   */
  createWorkflow(
    _name: string,
    definition: WorkflowDefinition,
    context: Record<string, any> = {}
  ): Workflow {
    const workflow: Workflow = {
      id: this.generateId(),
      name: definition.name,
      description: definition.description,
      steps: definition.steps.map((step, index) => ({
        id: `step-${index + 1}`,
        agent: step.agent,
        task: step.task,
        dependsOn: step.dependsOn,
        status: 'pending'
      })),
      context,
      status: 'pending',
      createdAt: new Date(),
      updatedAt: new Date()
    };

    this.activeWorkflows.set(workflow.id, workflow);
    this.saveWorkflow(workflow);

    this.emit('workflow:created', workflow);
    return workflow;
  }

  /**
   * Execute a workflow
   */
  async executeWorkflow(workflowId: string): Promise<Workflow> {
    const workflow = this.activeWorkflows.get(workflowId);
    if (!workflow) {
      throw new Error(`Workflow ${workflowId} not found`);
    }

    workflow.status = 'running';
    workflow.updatedAt = new Date();
    this.saveWorkflow(workflow);

    this.emit('workflow:started', workflow);

    try {
      for (const step of workflow.steps) {
        if (step.status === 'pending') {
          await this.executeStep(workflow, step);
        }
      }

      workflow.status = 'completed';
      workflow.updatedAt = new Date();
      this.saveWorkflow(workflow);

      this.emit('workflow:completed', workflow);
    } catch (error) {
      workflow.status = 'failed';
      workflow.updatedAt = new Date();
      this.saveWorkflow(workflow);

      this.emit('workflow:failed', workflow, error);
      throw error;
    }

    return workflow;
  }

  /**
   * Execute a single step
   */
  private async executeStep(workflow: Workflow, step: WorkflowStep): Promise<void> {
    // Check dependencies
    if (step.dependsOn && step.dependsOn.length > 0) {
      const depsCompleted = step.dependsOn.every(depId => {
        const dep = workflow.steps.find(s => s.id === depId);
        return dep?.status === 'completed';
      });

      if (!depsCompleted) {
        step.status = 'skipped';
        step.error = 'Dependencies not completed';
        this.saveWorkflow(workflow);
        return;
      }
    }

    step.status = 'running';
    step.startedAt = new Date();
    workflow.updatedAt = new Date();
    this.saveWorkflow(workflow);

    this.emit('step:started', workflow, step);

    try {
      // Simulate agent execution (in real implementation, this would call the agent)
      const result = await this.executeAgent(step.agent, step.task, workflow.context);

      step.status = 'completed';
      step.output = result;
      step.completedAt = new Date();
      workflow.updatedAt = new Date();
      this.saveWorkflow(workflow);

      this.emit('step:completed', workflow, step);
    } catch (error) {
      step.status = 'failed';
      step.error = error instanceof Error ? error.message : String(error);
      step.completedAt = new Date();
      workflow.updatedAt = new Date();
      this.saveWorkflow(workflow);

      this.emit('step:failed', workflow, step, error);
      throw error;
    }
  }

  /**
   * Execute agent (placeholder for actual implementation)
   */
  private async executeAgent(
    agent: string,
    task: string,
    _context: Record<string, any>
  ): Promise<string> {
    // This would be replaced with actual agent invocation
    return `Agent ${agent} completed task: ${task}`;
  }

  /**
   * Save workflow state
   */
  private saveWorkflow(workflow: Workflow): void {
    const workflowPath = path.join(this.workflowsDir, `${workflow.id}.json`);
    fs.writeFileSync(workflowPath, JSON.stringify(workflow, null, 2));
  }

  /**
   * Load workflow
   */
  loadWorkflow(workflowId: string): Workflow | null {
    const workflowPath = path.join(this.workflowsDir, `${workflowId}.json`);
    if (fs.existsSync(workflowPath)) {
      const data = fs.readFileSync(workflowPath, 'utf-8');
      return JSON.parse(data);
    }
    return null;
  }

  /**
   * Get workflow status
   */
  getWorkflowStatus(workflowId: string): {
    status: string;
    progress: { completed: number; total: number };
    steps: WorkflowStep[];
  } | null {
    const workflow = this.loadWorkflow(workflowId);
    if (!workflow) return null;

    const completed = workflow.steps.filter(s => s.status === 'completed').length;

    return {
      status: workflow.status,
      progress: { completed, total: workflow.steps.length },
      steps: workflow.steps
    };
  }

  /**
   * Resume a failed workflow
   */
  async resumeWorkflow(workflowId: string): Promise<Workflow> {
    const workflow = this.loadWorkflow(workflowId);
    if (!workflow) {
      throw new Error(`Workflow ${workflowId} not found`);
    }

    // Reset failed steps to pending
    workflow.steps.forEach(step => {
      if (step.status === 'failed') {
        step.status = 'pending';
        step.error = undefined;
        step.output = undefined;
      }
    });

    workflow.status = 'running';
    workflow.updatedAt = new Date();
    this.activeWorkflows.set(workflow.id, workflow);
    this.saveWorkflow(workflow);

    return this.executeWorkflow(workflowId);
  }

  /**
   * Cancel a running workflow
   */
  cancelWorkflow(workflowId: string): boolean {
    const workflow = this.loadWorkflow(workflowId);
    if (!workflow) return false;

    if (workflow.status === 'running') {
      workflow.status = 'failed';
      workflow.updatedAt = new Date();
      this.saveWorkflow(workflow);
      this.emit('workflow:cancelled', workflow);
      return true;
    }

    return false;
  }

  /**
   * List all workflows
   */
  listWorkflows(): Array<{
    id: string;
    name: string;
    status: string;
    createdAt: Date;
    updatedAt: Date;
  }> {
    const workflows: Array<{
      id: string;
      name: string;
      status: string;
      createdAt: Date;
      updatedAt: Date;
    }> = [];

    if (fs.existsSync(this.workflowsDir)) {
      const files = fs.readdirSync(this.workflowsDir);
      for (const file of files) {
        if (file.endsWith('.json')) {
          const data = fs.readFileSync(
            path.join(this.workflowsDir, file),
            'utf-8'
          );
          const workflow = JSON.parse(data);
          workflows.push({
            id: workflow.id,
            name: workflow.name,
            status: workflow.status,
            createdAt: workflow.createdAt,
            updatedAt: workflow.updatedAt
          });
        }
      }
    }

    return workflows.sort((a, b) => 
      new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
    );
  }

  /**
   * Get predefined workflow definitions
   */
  getPredefinedWorkflows(): Record<string, WorkflowDefinition> {
    return {
      'landing-page': {
        name: 'Landing Page',
        description: 'Build a complete landing page with design and implementation',
        steps: [
          {
            agent: '@design-system-consultant',
            task: 'Design color palette, typography, and component specs'
          },
          {
            agent: '@frontend-developer',
            task: 'Build landing page components with design specs',
            dependsOn: ['step-1']
          }
        ]
      },
      'saas-mvp': {
        name: 'SaaS MVP',
        description: 'Build a complete SaaS MVP from scratch',
        steps: [
          {
            agent: '@product-manager',
            task: 'Define MVP scope and user stories'
          },
          {
            agent: '@api-architect',
            task: 'Design API endpoints and data models',
            dependsOn: ['step-1']
          },
          {
            agent: '@backend-architect',
            task: 'Implement database schema and API endpoints',
            dependsOn: ['step-2']
          },
          {
            agent: '@frontend-developer',
            task: 'Build frontend UI and integrate with API',
            dependsOn: ['step-3']
          },
          {
            agent: '@testing-reality-checker',
            task: 'Test the complete MVP and validate functionality',
            dependsOn: ['step-4']
          }
        ]
      },
      'api-service': {
        name: 'API Service',
        description: 'Create a complete API service',
        steps: [
          {
            agent: '@api-architect',
            task: 'Design REST/GraphQL API specification'
          },
          {
            agent: '@backend-architect',
            task: 'Implement API endpoints and business logic',
            dependsOn: ['step-1']
          },
          {
            agent: '@testing-api-tester',
            task: 'Test all API endpoints',
            dependsOn: ['step-2']
          }
        ]
      },
      'fullstack-app': {
        name: 'Full-Stack Application',
        description: 'Build a complete full-stack application',
        steps: [
          {
            agent: '@fullstack-developer',
            task: 'Scaffold full-stack project with chosen framework'
          },
          {
            agent: '@design-system-consultant',
            task: 'Design UI system and component library',
            dependsOn: ['step-1']
          },
          {
            agent: '@fullstack-developer',
            task: 'Implement frontend and backend integration',
            dependsOn: ['step-2']
          },
          {
            agent: '@testing-reality-checker',
            task: 'End-to-end testing and validation',
            dependsOn: ['step-3']
          }
        ]
      },
      'auth-system': {
        name: 'Authentication System',
        description: 'Implement complete authentication',
        steps: [
          {
            agent: '@security-engineer',
            task: 'Design authentication flow and security model'
          },
          {
            agent: '@fullstack-developer',
            task: 'Implement authentication UI and API',
            dependsOn: ['step-1']
          },
          {
            agent: '@security-engineer',
            task: 'Security review and penetration testing',
            dependsOn: ['step-2']
          }
        ]
      }
    };
  }

  /**
   * Run a predefined workflow
   */
  async runPredefinedWorkflow(
    workflowName: string,
    context: Record<string, any> = {}
  ): Promise<Workflow> {
    const workflows = this.getPredefinedWorkflows();
    const definition = workflows[workflowName];

    if (!definition) {
      throw new Error(`Workflow "${workflowName}" not found`);
    }

    const workflow = this.createWorkflow(workflowName, definition, context);
    return this.executeWorkflow(workflow.id);
  }

  /**
   * Generate unique ID
   */
  private generateId(): string {
    return `wf-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  }
}

// Export singleton
export const workflowEngine = new WorkflowEngine();
