/**
 * @fileoverview Workflow Detection Engine
 *
 * Analyzes user requests to automatically select the appropriate workflow based on:
 * - Keyword matching
 * - Complexity estimation
 * - Workflow criteria
 *
 * @module workflow-detector
 */

import { readFileSync, existsSync, readdirSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { getPhaseSequence } from '../state/phase-state-machine.js';

const __dirname = dirname(fileURLToPath(import.meta.url));

/**
 * Get framework root directory
 * @param {string} projectPath - Project path
 * @returns {string} Framework root path
 */
function getFrameworkRoot(projectPath) {
  // Try npm-installed package first (only if it has the workflow configs)
  const npmPath = join(projectPath, 'node_modules/@polymorphism-tech/morph-spec');
  if (existsSync(npmPath) && existsSync(join(npmPath, 'framework/workflows/configs'))) {
    return npmPath;
  }

  // Fallback to local development path (from src/core/workflows/ → root)
  return join(__dirname, '..', '..', '..');
}

/**
 * Load all workflow configurations
 * @param {string} projectPath - Project path
 * @returns {Array<Object>} Array of workflow configs
 */
function loadWorkflowConfigs(projectPath) {
  const frameworkRoot = getFrameworkRoot(projectPath);
  const configsDir = join(frameworkRoot, 'framework/workflows/configs');

  if (!existsSync(configsDir)) {
    return [];
  }

  const configFiles = readdirSync(configsDir).filter(f => f.endsWith('.json'));
  const configs = [];

  for (const file of configFiles) {
    try {
      const configPath = join(configsDir, file);
      const content = readFileSync(configPath, 'utf8');
      configs.push(JSON.parse(content));
    } catch (err) {
      // Skip invalid configs
      continue;
    }
  }

  return configs;
}

/**
 * Match keywords in user request
 * @param {string} userRequest - User request text
 * @param {Array<string>} keywords - Workflow keywords
 * @returns {{matched: Array<string>, score: number}}
 */
function matchKeywords(userRequest, keywords) {
  const requestLower = userRequest.toLowerCase();
  const matched = [];

  for (const keyword of keywords) {
    const keywordLower = keyword.toLowerCase();

    // Check for partial matches (e.g., "paginação" matches "pagination")
    if (requestLower.includes(keywordLower)) {
      matched.push(keyword);
    }
  }

  // Score: percentage of keywords matched
  const score = matched.length > 0 ? matched.length / keywords.length : 0;

  return { matched, score };
}

/**
 * Estimate complexity from user request
 * @param {string} userRequest - User request text
 * @returns {{files: number, lines: number, components: number, hasInfra: boolean}}
 */
function estimateComplexity(userRequest) {
  const requestLower = userRequest.toLowerCase();

  // Scope indicators
  const scopeIndicators = {
    trivial: ['button', 'cor', 'color', 'css', 'fix', 'typo', 'edge case', 'one'],
    small: ['add', 'create', 'single', 'simple', 'quick'],
    medium: ['feature', 'pagination', 'filter', 'export', 'refactor', 'optimize'],
    large: ['system', 'complete', 'all', 'multiple', 'integration', 'architecture'],
    huge: ['platform', 'infrastructure', 'subsystem', 'migrate', 'rebuild']
  };

  // Infrastructure indicators
  const infraKeywords = ['bicep', 'azure', 'deploy', 'docker', 'kubernetes', 'infrastructure', 'terraform'];
  const hasInfra = infraKeywords.some(kw => requestLower.includes(kw));

  // Component indicators
  const componentKeywords = ['component', 'page', 'screen', 'form', 'table', 'chart', 'modal'];
  const componentMatches = componentKeywords.filter(kw => requestLower.includes(kw)).length;

  // Entity indicators
  const entityKeywords = ['user', 'order', 'product', 'customer', 'invoice', 'report'];
  const entityMatches = entityKeywords.filter(kw => requestLower.includes(kw)).length;

  // Determine scope
  let scope = 'medium';
  for (const [level, indicators] of Object.entries(scopeIndicators)) {
    if (indicators.some(indicator => requestLower.includes(indicator))) {
      scope = level;
      break;
    }
  }

  // Estimate based on scope
  const estimates = {
    trivial: { files: 1, lines: 30, components: 0 },
    small: { files: 2, lines: 100, components: 1 },
    medium: { files: 5, lines: 200, components: 1 },
    large: { files: 12, lines: 800, components: 3 },
    huge: { files: 25, lines: 2000, components: 5 }
  };

  const base = estimates[scope] || estimates.medium;

  // Adjust based on entities and components mentioned
  const files = base.files + entityMatches + componentMatches;
  const lines = base.lines + (entityMatches * 50) + (componentMatches * 100);
  const components = base.components + componentMatches;

  return {
    files,
    lines,
    components,
    hasInfra
  };
}

/**
 * Check if complexity matches workflow criteria
 * @param {Object} complexity - Estimated complexity
 * @param {Object} criteria - Workflow criteria
 * @returns {{matches: boolean, score: number}}
 */
function matchComplexity(complexity, criteria) {
  let matches = 0;
  let total = 0;

  // File count
  if (criteria.maxFiles !== undefined) {
    total++;
    if (complexity.files <= criteria.maxFiles) matches++;
  }
  if (criteria.minFiles !== undefined) {
    total++;
    if (complexity.files >= criteria.minFiles) matches++;
  }

  // Line count
  if (criteria.maxLines !== undefined) {
    total++;
    if (complexity.lines <= criteria.maxLines) matches++;
  }
  if (criteria.minLines !== undefined) {
    total++;
    if (complexity.lines >= criteria.minLines) matches++;
  }

  // Component count
  if (criteria.maxComponents !== undefined) {
    total++;
    if (complexity.components <= criteria.maxComponents) matches++;
  }
  if (criteria.minComponents !== undefined) {
    total++;
    if (complexity.components >= criteria.minComponents) matches++;
  }

  // Infrastructure
  if (criteria.noInfrastructure !== undefined) {
    total++;
    if (criteria.noInfrastructure === !complexity.hasInfra) matches++;
  }
  if (criteria.infrastructure !== undefined) {
    total++;
    if (criteria.infrastructure === complexity.hasInfra) matches++;
  }

  const score = total > 0 ? matches / total : 0.5;
  const meetsAll = total > 0 && matches === total;

  return { matches: meetsAll, score };
}

/**
 * Detect workflow for a feature request
 * @param {Object} options - Detection options
 * @param {string} options.userRequest - User's feature request
 * @param {string} [options.projectPath='.'] - Project path
 * @param {string} [options.featureName] - Feature name (optional)
 * @returns {Object} Detection result
 */
export async function detectWorkflow(options) {
  const { userRequest, projectPath = '.', featureName } = options;

  if (!userRequest || userRequest.trim().length === 0) {
    throw new Error('userRequest is required');
  }

  // Load all workflow configs
  const workflows = loadWorkflowConfigs(projectPath);

  if (workflows.length === 0) {
    // Fallback if no configs found
    return {
      workflowId: 'full-morph',
      confidence: 0.5,
      matchedKeywords: [],
      estimatedComplexity: { files: 0, lines: 0, components: 0, hasInfra: false },
      reasoning: 'No workflow configs found - using full-morph as fallback',
      alternativeWorkflows: []
    };
  }

  // Estimate complexity
  const complexity = estimateComplexity(userRequest);

  // Score each workflow
  const scores = [];

  for (const workflow of workflows) {
    const keywordMatch = matchKeywords(userRequest, workflow.keywords || []);
    const complexityMatch = matchComplexity(complexity, workflow.criteria || {});

    // Weighted scoring
    const keywordWeight = 0.4;
    const complexityWeight = 0.3;
    const priorityWeight = 0.2;
    const contextWeight = 0.1;

    // Priority score (lower number = higher priority = higher score)
    const priorityScore = workflow.priority ? (4 - workflow.priority) / 3 : 0.5;

    // Context score (checks for special conditions)
    let contextScore = 0.5;
    if (workflow.criteria?.hasPrototype && (userRequest.toLowerCase().includes('prototype') || userRequest.toLowerCase().includes('mockup'))) {
      contextScore = 1.0;
    }
    if (workflow.criteria?.hasExistingPages && userRequest.toLowerCase().includes('existing')) {
      contextScore = 1.0;
    }

    // Calculate total score
    let totalScore =
      (keywordMatch.score * keywordWeight) +
      (complexityMatch.score * complexityWeight) +
      (priorityScore * priorityWeight) +
      (contextScore * contextWeight);

    // Penalty for workflows with no keyword matches (unless it's full-morph fallback)
    if (keywordMatch.matched.length === 0 && workflow.id !== 'full-morph') {
      totalScore *= 0.5; // 50% penalty for no keyword match
    }

    // Boost for strong keyword matches (3+ keywords matched)
    if (keywordMatch.matched.length >= 3) {
      totalScore *= 1.2; // 20% boost
    }

    scores.push({
      workflowId: workflow.id,
      workflowName: workflow.name,
      score: totalScore,
      keywordMatch: keywordMatch.matched,
      complexityMatch: complexityMatch.matches,
      reasoning: []
    });
  }

  // Sort by score (highest first)
  scores.sort((a, b) => b.score - a.score);

  // Get top result
  const topResult = scores[0];
  const confidence = topResult.score;

  // Build reasoning
  const reasoning = [];
  if (topResult.keywordMatch.length > 0) {
    reasoning.push(`Matched keyword${topResult.keywordMatch.length > 1 ? 's' : ''}: ${topResult.keywordMatch.join(', ')}`);
  }
  reasoning.push(`Estimated ${complexity.files} files, ~${complexity.lines} lines`);
  if (complexity.hasInfra) {
    reasoning.push('Infrastructure keywords detected');
  }
  if (topResult.complexityMatch) {
    reasoning.push('Meets all complexity criteria');
  }

  // Get alternatives (next 2 workflows with score > 0.2)
  const alternatives = scores.slice(1, 3)
    .filter(w => w.score > 0.2)
    .map(w => ({
      workflowId: w.workflowId,
      confidence: w.score,
      reason: `${w.workflowName} (${Math.round(w.score * 100)}% match)`
    }));

  return {
    workflowId: topResult.workflowId,
    confidence,
    matchedKeywords: topResult.keywordMatch,
    estimatedComplexity: complexity,
    reasoning: reasoning.join(' + '),
    alternativeWorkflows: alternatives
  };
}

/**
 * Get workflow configuration by ID
 * @param {string} workflowId - Workflow ID
 * @param {string} [projectPath='.'] - Project path
 * @returns {Object|null} Workflow config or null
 */
export function getWorkflowConfig(workflowId, projectPath = '.') {
  const workflows = loadWorkflowConfigs(projectPath);
  const config = workflows.find(w => w.id === workflowId) || null;

  if (config) {
    const validPhases = new Set(getPhaseSequence());
    const declaredPhases = config.phases?.run || [];
    for (const phase of declaredPhases) {
      if (!validPhases.has(phase)) {
        console.warn(`[workflow] Warning: workflow "${workflowId}" declares unknown phase "${phase}" — ignored`);
      }
    }
  }

  return config;
}

/**
 * List all available workflows
 * @param {string} [projectPath='.'] - Project path
 * @returns {Array<{id: string, name: string, description: string}>}
 */
export function listWorkflows(projectPath = '.') {
  const workflows = loadWorkflowConfigs(projectPath);
  return workflows.map(w => ({
    id: w.id,
    name: w.name,
    description: w.description
  }));
}

// ============================================================================
// Parallel, Fusion, Long-Running, Zero-Touch Detection
// ============================================================================

/**
 * Detect if parallel execution mode should be used for a feature.
 * Based on dependency graph analysis: if feature has independent task squads,
 * recommend parallel mode.
 *
 * @param {string} feature - Feature name
 * @param {string} [projectPath='.'] - Project path
 * @returns {Object} { useParallel: boolean, reason: string, squads: Array }
 */
export async function detectParallelMode(feature, projectPath = '.') {
  try {
    const { getExecutionPlan } = await import('../../lib/threads/thread-coordinator.js').catch(() => {
      // thread-coordinator may not exist yet — graceful fallback
      return { getExecutionPlan: null };
    });

    if (!getExecutionPlan) {
      return { useParallel: false, reason: 'thread-coordinator not available', squads: [] };
    }

    const plan = getExecutionPlan(feature);

    if (!plan.valid) {
      return { useParallel: false, reason: plan.error, squads: [] };
    }

    const parallelPhases = plan.phases.filter(p => p.canRunParallel);
    const useParallel = parallelPhases.length > 0 && plan.stats.parallelizationRatio > 30;

    return {
      useParallel,
      reason: useParallel
        ? `${plan.stats.parallelizationRatio}% of tasks are parallelizable across ${parallelPhases.length} phases`
        : 'Tasks are mostly sequential — parallel mode would not improve throughput',
      parallelizationRatio: plan.stats.parallelizationRatio,
      parallelPhases: parallelPhases.length,
      squads: parallelPhases.map(p => p.tasks)
    };
  } catch {
    return { useParallel: false, reason: 'Dependency analysis failed', squads: [] };
  }
}

/**
 * Detect if a request needs fusion workflow (uncertainty → run N agents → best-of-N)
 * @param {string} userRequest
 * @returns {boolean}
 */
export function detectFusionNeed(userRequest) {
  const fusionKeywords = [
    'uncertain', 'uncertainty', 'critical decision', 'compare approaches',
    'best approach', 'prototype', 'explore options', 'which is better',
    'not sure how', 'multiple solutions', 'benchmark'
  ];
  const req = userRequest.toLowerCase();
  return fusionKeywords.some(kw => req.includes(kw));
}

/**
 * Detect if a request needs long-running workflow (L-Thread with stop hooks)
 * @param {string} userRequest
 * @returns {boolean}
 */
export function detectLongRunningNeed(userRequest) {
  const longRunningKeywords = [
    'autonomous', 'large scope', 'full system', 'end to end', 'e2e',
    'complete implementation', 'no interruption', 'long running', 'unattended'
  ];
  const req = userRequest.toLowerCase();
  const fileCountMatch = req.match(/(\d+)\+?\s+files?/);
  const fileCount = fileCountMatch ? parseInt(fileCountMatch[1]) : 0;
  return longRunningKeywords.some(kw => req.includes(kw)) || fileCount > 10;
}

/**
 * Check zero-touch eligibility for a feature (requires trust-manager)
 * @param {string} feature
 * @returns {Promise<boolean>}
 */
export async function checkZeroTouchEligibility(feature) {
  try {
    const { getTrustLevel } = await import('../../lib/trust/trust-manager.js').catch(() => ({
      getTrustLevel: null
    }));

    if (!getTrustLevel) return false;

    const level = getTrustLevel(feature);
    return level === 'maximum';
  } catch {
    return false;
  }
}
