import type { Command } from 'commander';
import * as path from 'node:path';
import { existsSync, readdirSync } from 'node:fs';
import { rm } from 'node:fs/promises';
import { select, isCancel } from '@clack/prompts';
import { loadWorkflowModule, isWorkflowTemplate, WorkflowTemplate } from '../../workflows/index.js';
import { hasTemplateChanged, setActiveTemplate } from '../../shared/workflows/index.js';
import { ensureWorkspaceStructure, mirrorSubAgents } from '../../runtime/services/workspace/index.js';
import type { SelectionChoice } from '../utils/selection-menu.js';
import { isModuleStep } from '../../workflows/templates/types.js';
import { getAllInstalledImports } from '../../shared/imports/index.js';
import { registerImportedAgents, clearImportedAgents } from '../../workflows/utils/config.js';
import { getDevRoot } from '../../shared/runtime/dev.js';

export function printAvailableWorkflowTemplatesHeading(): void {
  console.log('\nAvailable workflow templates:\n');
}

export interface TemplateChoice extends SelectionChoice<string> {
  source?: string; // Import package name for imported templates
  stepCount: number;
}


async function handleTemplateSelectionSuccess(template: WorkflowTemplate, templateFilePath: string): Promise<void> {
  const templateFileName = path.basename(templateFilePath);
  const cwd = process.env.CODEMACHINE_CWD || process.cwd();
  const cmRoot = path.join(cwd, '.codemachine');
  const agentsDir = path.join(cmRoot, 'agents');

  console.log(`\nSelected: ${template.name}`);
  console.log(`Template path: ${path.relative(process.cwd(), templateFilePath)}`);
  console.log(`\nSteps:`);

  template.steps.forEach((step, index) => {
    if (isModuleStep(step)) {
      console.log(`  ${index + 1}. ${step.agentName} [${step.agentId}]`);
    } else {
      console.log(`  ${index + 1}. [UI Element]`);
    }
  });

  // Ensure workspace structure exists first (needed for template tracking file)
  await ensureWorkspaceStructure({ cwd });

  // Check if template changed and regenerate agents folder
  const changed = await hasTemplateChanged(cmRoot, templateFileName);

  // Update active template tracking (pass autonomousMode from template if defined)
  await setActiveTemplate(cmRoot, templateFileName, template.autonomousMode);

  if (changed) {
    console.log('\n🔄 Template changed, regenerating agents...');

    // Delete existing agents folder if it exists
    if (existsSync(agentsDir)) {
      await rm(agentsDir, { recursive: true, force: true });
    }

    // Mirror sub-agents if template has subAgentIds
    if (template.subAgentIds && template.subAgentIds.length > 0) {
      await mirrorSubAgents({ cwd, subAgentIds: template.subAgentIds });
      console.log('✅ Agents regenerated successfully');
    } else {
      console.log('✓ Template has no sub-agents to mirror');
    }
  } else {
    console.log('\n✓ Template unchanged, agents folder up to date');
  }

  console.log(`\n✅ Template saved to .codemachine/template.json`);
}

export async function getAvailableTemplates(): Promise<TemplateChoice[]> {
  const templates: TemplateChoice[] = [];

  // Clear any previously registered imported agents
  clearImportedAgents();

  // Pre-register all imported agents before loading any templates
  // This ensures resolveStep() can find agents from all imports
  const importedPackages = getAllInstalledImports();
  for (const imp of importedPackages) {
    registerImportedAgents(imp.resolvedPaths.config);
  }

  // Helper to load templates from a directory
  async function loadFromDir(
    dir: string,
    source?: string
  ): Promise<TemplateChoice[]> {
    const results: TemplateChoice[] = [];

    if (!existsSync(dir)) {
      return results;
    }

    const files = readdirSync(dir).filter(file =>
      file.endsWith('.workflow.js') && !file.startsWith('_example.')
    );

    for (const file of files) {
      try {
        const filePath = path.join(dir, file);
        const template = await loadWorkflowModule(filePath);

        if (isWorkflowTemplate(template)) {
          const stepCount = template.steps.length;
          const description = source
            ? `${stepCount} step(s) · from ${source}`
            : `${stepCount} step(s)`;

          results.push({
            title: template.name,
            value: filePath,
            description,
            source,
            stepCount,
          });
        }
      } catch (error) {
        // Skip invalid templates
        console.warn(`Warning: Could not load template ${file}:`, error);
      }
    }

    return results;
  }

  // Load templates from imported packages
  for (const imp of importedPackages) {
    templates.push(...await loadFromDir(imp.resolvedPaths.workflows, imp.name));
  }

  // Load dev root templates (if running from source)
  const devRoot = getDevRoot();
  if (devRoot) {
    const devTemplatesDir = path.resolve(devRoot, 'templates', 'workflows');
    templates.push(...await loadFromDir(devTemplatesDir));
  }

  // Sort alphabetically — flat list, no categories
  templates.sort((a, b) => a.title.localeCompare(b.title));

  return templates;
}

export async function selectTemplateByNumber(templateNumber: number): Promise<void> {
  try {
    const templates = await getAvailableTemplates();

    if (templates.length === 0) {
      console.log('No workflow templates found in templates/workflows/');
      return;
    }

    if (templateNumber < 1 || templateNumber > templates.length) {
      console.log(`Invalid selection. Please choose a number between 1 and ${templates.length}.`);
      return;
    }

    const selectedTemplate = templates[templateNumber - 1];
    const template = await loadWorkflowModule(selectedTemplate.value);

    if (isWorkflowTemplate(template)) {
      await handleTemplateSelectionSuccess(template, selectedTemplate.value);
    }
  } catch (error) {
    console.error('Error selecting template:', error instanceof Error ? error.message : String(error));
  }
}

export async function listTemplates(): Promise<void> {
  const templates = await getAvailableTemplates();

  if (templates.length === 0) {
    console.log('No workflow templates found.');
    return;
  }

  for (const t of templates) {
    console.log(t.title);
  }
}

export async function runTemplatesCommand(inSession: boolean = false): Promise<void> {
  try {
    const templates = await getAvailableTemplates();

    if (templates.length === 0) {
      console.log('No workflow templates found in templates/workflows/');
      return;
    }

    const options = templates.map(t => ({
      value: t.value,
      label: t.title,
      hint: t.source
        ? `${t.stepCount} steps · ${t.source}`
        : `${t.stepCount} steps`,
    }));

    console.log();
    const result = await select({
      message: 'Choose a workflow template:',
      options,
    });

    if (isCancel(result)) {
      console.log('No template selected.');
      return;
    }

    const selectedPath = result as string;
    const selectedTemplate = templates.find(t => t.value === selectedPath);
    const template = await loadWorkflowModule(selectedPath);
    if (isWorkflowTemplate(template)) {
      await handleTemplateSelectionSuccess(template, selectedPath);
    }
  } catch (error) {
    console.error('Error loading templates:', error);
    // Only exit if not in session mode
    if (!inSession) {
      process.exit(1);
    }
  }
}

export function registerTemplatesCommand(program: Command): void {
  const templates = program
    .command('templates')
    .description('List and select workflow templates')
    .action(runTemplatesCommand);

  templates
    .command('list')
    .description('List available template names')
    .action(listTemplates);
}
