/**
 * Template Registry - Discovery and metadata API for all framework templates.
 *
 * Provides programmatic access to REGISTRY.json for:
 * - Template discovery by category, phase, stack
 * - Template metadata lookup
 * - Path resolution with fallback logic
 * - Validation of template existence
 *
 * @module template-registry
 */

import { readFileSync, existsSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';

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

// Cache for loaded registry
let _cachedRegistry = null;

/**
 * Load the template registry from REGISTRY.json.
 *
 * @param {string} [projectPath] - Optional project path (defaults to framework root)
 * @returns {Object} Parsed registry object
 * @throws {Error} If registry file not found or invalid JSON
 */
export function loadTemplateRegistry(projectPath = null) {
  // Return cached if already loaded
  if (_cachedRegistry) {
    return _cachedRegistry;
  }

  // Default to framework registry
  const registryPath = projectPath
    ? join(projectPath, 'framework/templates/REGISTRY.json')
    : join(__dirname, '../../../framework/templates/REGISTRY.json');

  if (!existsSync(registryPath)) {
    throw new Error(`Template registry not found: ${registryPath}`);
  }

  try {
    const content = readFileSync(registryPath, 'utf-8');
    _cachedRegistry = JSON.parse(content);
    return _cachedRegistry;
  } catch (error) {
    throw new Error(`Failed to parse template registry: ${error.message}`);
  }
}

/**
 * Clear cached registry (useful for testing).
 */
export function clearRegistryCache() {
  _cachedRegistry = null;
}

/**
 * Get all templates from registry.
 *
 * @param {string} [projectPath] - Optional project path
 * @returns {Array} Array of template objects
 */
export function getAllTemplates(projectPath = null) {
  const registry = loadTemplateRegistry(projectPath);
  return registry.templates || [];
}

/**
 * Get template by ID.
 *
 * @param {string} templateId - Template identifier (e.g., 'proposal', 'spec')
 * @param {string} [projectPath] - Optional project path
 * @returns {Object|null} Template object or null if not found
 */
export function getTemplateById(templateId, projectPath = null) {
  const templates = getAllTemplates(projectPath);
  return templates.find(t => t.id === templateId) || null;
}

/**
 * Get templates by category.
 *
 * @param {string} category - Category ('documentation', 'code', 'infrastructure', 'context', 'examples')
 * @param {string} [projectPath] - Optional project path
 * @returns {Array} Filtered template array
 */
export function getTemplatesByCategory(category, projectPath = null) {
  const templates = getAllTemplates(projectPath);
  return templates.filter(t => t.category === category);
}

/**
 * Get templates by phase.
 *
 * @param {string} phase - Phase name (e.g., 'proposal', 'design', 'implement')
 * @param {string} [projectPath] - Optional project path
 * @returns {Array} Filtered template array
 */
export function getTemplatesByPhase(phase, projectPath = null) {
  const templates = getAllTemplates(projectPath);
  return templates.filter(t => t.phase === phase);
}

/**
 * Get templates by stack.
 *
 * @param {string} stackId - Stack identifier ('blazor-azure', 'nextjs-supabase')
 * @param {string} [projectPath] - Optional project path
 * @returns {Array} Filtered template array (includes universal + stack-specific)
 */
export function getTemplatesByStack(stackId, projectPath = null) {
  const templates = getAllTemplates(projectPath);
  return templates.filter(t => {
    // Include universal templates (stackSpecific: false)
    if (!t.stackSpecific) return true;

    // Include stack-specific if matches
    return t.stacks && t.stacks.includes(stackId);
  });
}

/**
 * Get required templates only.
 *
 * @param {string} [stackId] - Optional stack filter
 * @param {string} [projectPath] - Optional project path
 * @returns {Array} Filtered template array
 */
export function getRequiredTemplates(stackId = null, projectPath = null) {
  let templates = getAllTemplates(projectPath);

  // Filter by required flag
  templates = templates.filter(t => t.required === true);

  // Optional: filter by stack
  if (stackId) {
    templates = templates.filter(t => {
      if (!t.stackSpecific) return true;
      return t.stacks && t.stacks.includes(stackId);
    });
  }

  return templates;
}

/**
 * Get infrastructure templates for a specific stack.
 *
 * @param {string} stackId - Stack identifier
 * @param {string} [projectPath] - Optional project path
 * @returns {Array} Infrastructure template array
 */
export function getInfrastructureTemplates(stackId, projectPath = null) {
  const registry = loadTemplateRegistry(projectPath);
  return registry.infrastructureTemplates?.[stackId] || [];
}

/**
 * Get contract templates for a specific stack.
 *
 * @param {string} stackId - Stack identifier
 * @param {string} [projectPath] - Optional project path
 * @returns {Array} Contract template array
 */
export function getContractTemplates(stackId, projectPath = null) {
  const registry = loadTemplateRegistry(projectPath);
  return registry.contractTemplates?.[stackId] || [];
}

/**
 * Get integration templates for a specific stack.
 *
 * @param {string} stackId - Stack identifier
 * @param {string} [projectPath] - Optional project path
 * @returns {Array} Integration template array
 */
export function getIntegrationTemplates(stackId, projectPath = null) {
  const registry = loadTemplateRegistry(projectPath);
  return registry.integrationTemplates?.[stackId] || [];
}

/**
 * Get SaaS templates for a specific stack.
 *
 * @param {string} stackId - Stack identifier
 * @param {string} [projectPath] - Optional project path
 * @returns {Array} SaaS template array
 */
export function getSaasTemplates(stackId, projectPath = null) {
  const registry = loadTemplateRegistry(projectPath);
  return registry.saasTemplates?.[stackId] || [];
}

/**
 * Get example templates.
 *
 * @param {string} [projectPath] - Optional project path
 * @returns {Array} Example template array
 */
export function getExampleTemplates(projectPath = null) {
  const registry = loadTemplateRegistry(projectPath);
  return registry.exampleTemplates || [];
}

/**
 * Search templates by keyword (searches id, name, description).
 *
 * @param {string} keyword - Search term
 * @param {string} [projectPath] - Optional project path
 * @returns {Array} Matching templates
 */
export function searchTemplates(keyword, projectPath = null) {
  const templates = getAllTemplates(projectPath);
  const lowerKeyword = keyword.toLowerCase();

  return templates.filter(t => {
    return (
      t.id.toLowerCase().includes(lowerKeyword) ||
      t.name.toLowerCase().includes(lowerKeyword) ||
      (t.description && t.description.toLowerCase().includes(lowerKeyword))
    );
  });
}

/**
 * Get registry statistics.
 *
 * @param {string} [projectPath] - Optional project path
 * @returns {Object} Statistics object
 */
export function getRegistryStats(projectPath = null) {
  const registry = loadTemplateRegistry(projectPath);
  return registry.stats || {};
}

/**
 * Validate that a template exists in the registry.
 *
 * @param {string} templateId - Template identifier
 * @param {string} [projectPath] - Optional project path
 * @returns {boolean} True if template exists
 */
export function templateExists(templateId, projectPath = null) {
  return getTemplateById(templateId, projectPath) !== null;
}

/**
 * Get template absolute path with fallback logic.
 *
 * Resolution order:
 *   1. Stack-specific template (stacks/{stack}/.morph/templates/{path})
 *   2. Framework universal template (framework/templates/{path})
 *
 * @param {string} templateId - Template identifier
 * @param {string} projectPath - Project root path
 * @param {string} [stackId] - Stack identifier (optional)
 * @returns {string|null} Absolute template path or null if not found
 */
export function resolveTemplatePathById(templateId, projectPath, stackId = null) {
  const template = getTemplateById(templateId, projectPath);
  if (!template) {
    return null;
  }

  // If template is universal (framework location)
  if (template.location === 'framework') {
    const frameworkPath = join(projectPath, 'framework/templates', template.path);
    return existsSync(frameworkPath) ? frameworkPath : null;
  }

  // If template is stack-specific
  if (template.location === 'stack' && stackId) {
    // Try stack-specific first
    const stackPath = join(projectPath, 'stacks', stackId, '.morph/templates', template.path);
    if (existsSync(stackPath)) {
      return stackPath;
    }

    // Fallback to framework if exists
    const frameworkPath = join(projectPath, 'framework/templates', template.path);
    if (existsSync(frameworkPath)) {
      return frameworkPath;
    }
  }

  return null;
}

/**
 * List all template IDs.
 *
 * @param {string} [projectPath] - Optional project path
 * @returns {string[]} Array of template IDs
 */
export function listTemplateIds(projectPath = null) {
  const templates = getAllTemplates(projectPath);
  return templates.map(t => t.id);
}

/**
 * Get templates that use Handlebars engine.
 *
 * @param {string} [projectPath] - Optional project path
 * @returns {Array} Templates using Handlebars
 */
export function getHandlebarsTemplates(projectPath = null) {
  const templates = getAllTemplates(projectPath);
  return templates.filter(t => t.engine === 'handlebars');
}

/**
 * Get templates that use custom engine (need migration).
 *
 * @param {string} [projectPath] - Optional project path
 * @returns {Array} Templates using custom engine
 */
export function getCustomEngineTemplates(projectPath = null) {
  const templates = getAllTemplates(projectPath);
  return templates.filter(t => t.engine === 'custom');
}

/**
 * Get deprecated templates.
 *
 * @param {string} [projectPath] - Optional project path
 * @returns {Array} Deprecated templates
 */
export function getDeprecatedTemplates(projectPath = null) {
  const templates = getAllTemplates(projectPath);
  return templates.filter(t => t.deprecated === true);
}
