/**
 * Stack Resolver - Centralized stack path resolution for multi-stack support.
 *
 * Detects the active stack and resolves all stack-relative paths.
 * Resolution order: explicit > config.json > state.json > auto-detect > fallback
 *
 * @module stack-resolver
 */

import { readFileSync, existsSync, readdirSync } from 'fs';
import { join } from 'path';

const STACK_TYPE_TO_DIR = {
  'blazor-server': 'blazor-azure',
  'blazor-azure': 'blazor-azure',
  'nextjs-supabase': 'nextjs-supabase',
};

// Module-level override set by CLI --stack flag via setGlobalStack()
let _globalStackOverride = null;

/**
 * Set a global stack override (called by CLI --stack flag).
 * Once set, all resolveStackId() calls use this value unless
 * an explicit parameter is provided.
 * @param {string} stackId - Stack identifier
 */
export function setGlobalStack(stackId) {
  _globalStackOverride = stackId;
}

/**
 * Detect which stack directory to use.
 *
 * Resolution order:
 *   1. explicitStack parameter (from CLI --stack flag)
 *   2. .morph/config/config.json → project.type
 *   3. .morph/state.json → project.type
 *   4. Auto-detect: scan stacks/ for existing directories
 *   5. Fallback: 'blazor-azure'
 *
 * @param {string} projectPath - Absolute path to project root
 * @param {string} [explicitStack] - Override from CLI --stack flag
 * @returns {string} Stack directory name (e.g., 'blazor-azure', 'nextjs-supabase')
 */
export function resolveStackId(projectPath, explicitStack) {
  // 1. Explicit parameter override
  if (explicitStack) {
    return STACK_TYPE_TO_DIR[explicitStack] || explicitStack;
  }

  // 1b. Global CLI --stack override
  if (_globalStackOverride) {
    return STACK_TYPE_TO_DIR[_globalStackOverride] || _globalStackOverride;
  }

  // 2. config.json → project.type
  try {
    const configPath = join(projectPath, '.morph/config/config.json');
    if (existsSync(configPath)) {
      const config = JSON.parse(readFileSync(configPath, 'utf8'));
      const projectType = config?.project?.type;
      if (projectType && STACK_TYPE_TO_DIR[projectType]) {
        return STACK_TYPE_TO_DIR[projectType];
      }
    }
  } catch { /* continue to next source */ }

  // 3. state.json → project.type
  try {
    const statePath = join(projectPath, '.morph/state.json');
    if (existsSync(statePath)) {
      const state = JSON.parse(readFileSync(statePath, 'utf8'));
      const projectType = state?.project?.type;
      if (projectType && STACK_TYPE_TO_DIR[projectType]) {
        return STACK_TYPE_TO_DIR[projectType];
      }
    }
  } catch { /* continue to next source */ }

  // 4. Auto-detect: first directory in stacks/
  try {
    const stacksDir = join(projectPath, 'stacks');
    if (existsSync(stacksDir)) {
      const dirs = readdirSync(stacksDir, { withFileTypes: true })
        .filter(d => d.isDirectory())
        .map(d => d.name);
      if (dirs.length === 1) {
        return dirs[0];
      }
    }
  } catch { /* continue to fallback */ }

  // 5. Fallback
  return 'blazor-azure';
}

/**
 * Get absolute path to the active stack's root directory.
 * @param {string} projectPath
 * @param {string} [explicitStack]
 * @returns {string}
 */
export function resolveStackPath(projectPath, explicitStack) {
  return join(projectPath, 'stacks', resolveStackId(projectPath, explicitStack));
}

/**
 * Get absolute path to the active stack's agents.json.
 * @param {string} projectPath
 * @param {string} [explicitStack]
 * @returns {string}
 */
export function resolveAgentsConfigPath(projectPath, explicitStack) {
  return join(resolveStackPath(projectPath, explicitStack), '.morph/config/agents.json');
}

/**
 * Get absolute path to the active stack's standards directory.
 * @param {string} projectPath
 * @param {string} [explicitStack]
 * @returns {string}
 */
export function resolveStandardsDir(projectPath, explicitStack) {
  return join(resolveStackPath(projectPath, explicitStack), '.morph/standards');
}

/**
 * Get absolute path to the active stack's templates directory.
 * @param {string} projectPath
 * @param {string} [explicitStack]
 * @returns {string}
 */
export function resolveTemplatesDir(projectPath, explicitStack) {
  return join(resolveStackPath(projectPath, explicitStack), '.morph/templates');
}

/**
 * Resolve a template file path with fallback logic.
 *
 * Resolution order:
 *   1. Stack-specific template in stacks/{stack}/.morph/templates/{templatePath}
 *   2. Universal template in framework/templates/{templatePath}
 *
 * @param {string} projectPath - Absolute path to project root
 * @param {string} templatePath - Relative template path (e.g., 'docs/proposal.md')
 * @param {string} [explicitStack] - Override from CLI --stack flag
 * @returns {string|null} Absolute path to template file, or null if not found
 */
export function resolveTemplatePath(projectPath, templatePath, explicitStack) {
  // 1. Check stack-specific template first
  const stackTemplatePath = join(
    resolveTemplatesDir(projectPath, explicitStack),
    templatePath
  );
  if (existsSync(stackTemplatePath)) {
    return stackTemplatePath;
  }

  // 2. Fallback to universal template in framework/
  const frameworkTemplatePath = join(projectPath, 'framework/templates', templatePath);
  if (existsSync(frameworkTemplatePath)) {
    return frameworkTemplatePath;
  }

  // 3. Not found in either location
  return null;
}

/**
 * Get absolute path to the active stack's config directory.
 * @param {string} projectPath
 * @param {string} [explicitStack]
 * @returns {string}
 */
export function resolveConfigDir(projectPath, explicitStack) {
  return join(resolveStackPath(projectPath, explicitStack), '.morph/config');
}

export { STACK_TYPE_TO_DIR };
