#!/usr/bin/env node

/**
 * MORPH-SPEC Task Manager
 *
 * Manages task completion, dependencies, checkpoints, and progress tracking.
 * Part of MORPH-SPEC 3.0 - Event-driven state management.
 */

const fs = require('fs').promises;
const path = require('path');
const chalk = require('chalk');

class TaskManager {
  constructor(statePath = '.morph/state.json') {
    this.statePath = statePath;
  }

  /**
   * Load state.json
   */
  async loadState() {
    try {
      const content = await fs.readFile(this.statePath, 'utf-8');
      return JSON.parse(content);
    } catch (error) {
      if (error.code === 'ENOENT') {
        throw new Error(`State file not found: ${this.statePath}. Run 'npx morph-spec state init' first.`);
      }
      throw error;
    }
  }

  /**
   * Save state.json
   */
  async saveState(state) {
    state.project.updatedAt = new Date().toISOString();
    await fs.writeFile(this.statePath, JSON.stringify(state, null, 2), 'utf-8');
  }

  /**
   * Complete one or more tasks
   */
  async completeTasks(featureName, taskIds) {
    const state = await this.loadState();
    const feature = state.features[featureName];

    if (!feature) {
      throw new Error(`Feature '${featureName}' not found in state.json`);
    }

    const results = [];

    for (const taskId of taskIds) {
      const task = feature.tasks.find(t => t.id === taskId);

      if (!task) {
        console.error(chalk.red(`❌ Task ${taskId} not found`));
        continue;
      }

      if (task.status === 'completed') {
        console.log(chalk.yellow(`⚠️  Task ${taskId} already completed`));
        continue;
      }

      // Validate dependencies
      const missingDeps = this.checkDependencies(task, feature.tasks);
      if (missingDeps.length > 0) {
        console.error(chalk.red(`❌ Cannot complete ${taskId}: missing dependencies: ${missingDeps.join(', ')}`));
        continue;
      }

      // Mark as completed
      task.status = 'completed';
      task.completedAt = new Date().toISOString();
      task.completedBy = 'claude';

      results.push(task);

      console.log(chalk.green(`✅ Task ${taskId} completed!`));

      // Register checkpoint if task has one
      if (task.checkpoint) {
        this.registerCheckpoint(feature, task);
      }
    }

    // Update progress
    feature.progress = this.calculateProgress(feature.tasks);

    // Auto-checkpoint every 3 tasks
    const recentCompleted = this.getRecentCompleted(feature.tasks, 3);
    if (recentCompleted.length === 3) {
      const lastCheckpoint = feature.checkpoints[feature.checkpoints.length - 1];
      const shouldAutoCheckpoint = !lastCheckpoint ||
        !recentCompleted.every(t =>
          lastCheckpoint.tasksCompleted.includes(t.id)
        );

      if (shouldAutoCheckpoint) {
        this.autoCheckpoint(feature, recentCompleted);
      }
    }

    // Save state
    await this.saveState(state);

    // Display progress
    this.displayProgress(feature);

    // Suggest next task
    const nextTask = this.getNextTask(feature.tasks);
    if (nextTask) {
      console.log(chalk.cyan(`\n⏭️  Next: ${nextTask.id} - ${nextTask.title}`));
      if (nextTask.dependencies && nextTask.dependencies.length > 0) {
        console.log(chalk.gray(`   Dependencies: ${nextTask.dependencies.join(', ')}`));
      }
    } else {
      console.log(chalk.green('\n🎉 All tasks completed!'));
    }

    return results;
  }

  /**
   * Check if task dependencies are met
   */
  checkDependencies(task, allTasks) {
    if (!task.dependencies || task.dependencies.length === 0) {
      return [];
    }

    return task.dependencies.filter(depId => {
      const dep = allTasks.find(t => t.id === depId);
      return !dep || dep.status !== 'completed';
    });
  }

  /**
   * Calculate progress
   */
  calculateProgress(tasks) {
    const total = tasks.length;
    const completed = tasks.filter(t => t.status === 'completed').length;
    const inProgress = tasks.filter(t => t.status === 'in_progress').length;
    const pending = tasks.filter(t => t.status === 'pending').length;
    const percentage = total > 0 ? Math.round((completed / total) * 100) : 0;

    return {
      total,
      completed,
      inProgress,
      pending,
      percentage
    };
  }

  /**
   * Register checkpoint (from task flag)
   */
  registerCheckpoint(feature, task) {
    const checkpoint = {
      id: task.checkpoint,
      timestamp: new Date().toISOString(),
      tasksCompleted: [task.id],
      note: `Checkpoint: ${task.title}`
    };

    feature.checkpoints = feature.checkpoints || [];
    feature.checkpoints.push(checkpoint);

    console.log(chalk.magenta(`\n🎯 ${task.checkpoint} reached!`));
  }

  /**
   * Auto-checkpoint every 3 tasks
   */
  autoCheckpoint(feature, tasks) {
    const checkpoint = {
      id: `CHECKPOINT_AUTO_${Date.now()}`,
      timestamp: new Date().toISOString(),
      tasksCompleted: tasks.map(t => t.id),
      note: `Auto-checkpoint: ${tasks.length} tasks completed`
    };

    feature.checkpoints = feature.checkpoints || [];
    feature.checkpoints.push(checkpoint);

    console.log(chalk.magenta(`\n🎉 Auto-checkpoint reached! ${tasks.length} tasks completed`));
  }

  /**
   * Get recently completed tasks (last N)
   */
  getRecentCompleted(tasks, count) {
    const completed = tasks
      .filter(t => t.status === 'completed' && t.completedAt)
      .sort((a, b) => new Date(b.completedAt) - new Date(a.completedAt));

    return completed.slice(0, count);
  }

  /**
   * Get next pending task (based on dependencies)
   */
  getNextTask(tasks) {
    const pending = tasks.filter(t => t.status === 'pending');

    // Find first task with all dependencies completed
    for (const task of pending) {
      const missingDeps = this.checkDependencies(task, tasks);
      if (missingDeps.length === 0) {
        return task;
      }
    }

    return null;
  }

  /**
   * Display progress
   */
  displayProgress(feature) {
    const { total, completed, inProgress, pending, percentage } = feature.progress;

    console.log(chalk.bold(`\n📊 Progress: ${percentage}% (${completed}/${total})`));
    console.log(chalk.gray(`   Completed: ${completed} | In Progress: ${inProgress} | Pending: ${pending}`));

    // Progress bar
    const barLength = 30;
    const filledLength = Math.round((percentage / 100) * barLength);
    const bar = '█'.repeat(filledLength) + '░'.repeat(barLength - filledLength);
    console.log(chalk.cyan(`   [${bar}] ${percentage}%`));
  }

  /**
   * Start a task (mark as in_progress)
   */
  async startTask(featureName, taskId) {
    const state = await this.loadState();
    const feature = state.features[featureName];

    if (!feature) {
      throw new Error(`Feature '${featureName}' not found`);
    }

    const task = feature.tasks.find(t => t.id === taskId);

    if (!task) {
      throw new Error(`Task ${taskId} not found`);
    }

    if (task.status === 'completed') {
      console.log(chalk.yellow(`⚠️  Task ${taskId} already completed`));
      return;
    }

    // Validate dependencies
    const missingDeps = this.checkDependencies(task, feature.tasks);
    if (missingDeps.length > 0) {
      throw new Error(`Cannot start ${taskId}: missing dependencies: ${missingDeps.join(', ')}`);
    }

    task.status = 'in_progress';
    task.startedAt = new Date().toISOString();

    await this.saveState(state);

    console.log(chalk.blue(`▶️  Task ${taskId} started: ${task.title}`));
  }

  /**
   * Get next task suggestion
   */
  async getNext(featureName) {
    const state = await this.loadState();
    const feature = state.features[featureName];

    if (!feature) {
      throw new Error(`Feature '${featureName}' not found`);
    }

    const nextTask = this.getNextTask(feature.tasks);

    if (nextTask) {
      console.log(chalk.cyan(`\n⏭️  Next task: ${nextTask.id} - ${nextTask.title}`));
      if (nextTask.dependencies && nextTask.dependencies.length > 0) {
        console.log(chalk.gray(`   Dependencies: ${nextTask.dependencies.join(', ')}`));
      }
      if (nextTask.files && nextTask.files.length > 0) {
        console.log(chalk.gray(`   Files:`));
        nextTask.files.forEach(f => console.log(chalk.gray(`     - ${f}`)));
      }
    } else {
      console.log(chalk.green('🎉 All tasks completed!'));
    }
  }
}

// CLI
async function main() {
  const args = process.argv.slice(2);
  const command = args[0];

  const manager = new TaskManager();

  try {
    switch (command) {
      case 'done':
      case 'complete': {
        const featureName = args[1];
        const taskIds = args.slice(2);

        if (!featureName || taskIds.length === 0) {
          console.error(chalk.red('Usage: npx morph-spec task done <feature> <task-id> [task-id...]'));
          process.exit(1);
        }

        await manager.completeTasks(featureName, taskIds);
        break;
      }

      case 'start': {
        const featureName = args[1];
        const taskId = args[2];

        if (!featureName || !taskId) {
          console.error(chalk.red('Usage: npx morph-spec task start <feature> <task-id>'));
          process.exit(1);
        }

        await manager.startTask(featureName, taskId);
        break;
      }

      case 'next': {
        const featureName = args[1];

        if (!featureName) {
          console.error(chalk.red('Usage: npx morph-spec task next <feature>'));
          process.exit(1);
        }

        await manager.getNext(featureName);
        break;
      }

      default:
        console.error(chalk.red(`Unknown command: ${command}`));
        console.log(chalk.gray('\nAvailable commands:'));
        console.log(chalk.gray('  done <feature> <task-id...>  - Mark tasks as completed'));
        console.log(chalk.gray('  start <feature> <task-id>    - Start a task (mark as in_progress)'));
        console.log(chalk.gray('  next <feature>               - Show next suggested task'));
        process.exit(1);
    }
  } catch (error) {
    console.error(chalk.red(`\n❌ Error: ${error.message}`));
    process.exit(1);
  }
}

if (require.main === module) {
  main();
}

module.exports = TaskManager;
