#!/usr/bin/env node

/**
 * Generate Library Reference Documentation
 *
 * This script scans the monorepo's libs/ directory, reads project.json files,
 * and generates the docs/library-reference.md file with organized library information.
 *
 * Usage: node tools/generate-library-reference.js
 */

const fs = require('fs');
const path = require('path');

// Configuration
const LIBS_DIR = path.join(__dirname, '..', 'libs');
const OUTPUT_FILE = path.join(__dirname, '..', 'docs', 'library-reference.md');
const PACKAGE_JSON_PATH = path.join(__dirname, '..', 'package.json');
const TSCONFIG_PATH = path.join(__dirname, '..', 'tsconfig.base.json');

// Domain configuration with display order
const DOMAIN_CONFIG = {
  availability: { order: 1, name: 'Availability Domain' },
  catalogue: { order: 2, name: 'Catalogue Domain' },
  checkout: { order: 3, name: 'Checkout Domain' },
  common: { order: 4, name: 'Common Libraries' },
  core: { order: 5, name: 'Core Libraries' },
  crm: { order: 6, name: 'CRM Domain' },
  icons: { order: 7, name: 'Icons' },
  oms: { order: 8, name: 'OMS Domain' },
  remission: { order: 9, name: 'Remission Domain' },
  shared: { order: 10, name: 'Shared Component Libraries' },
  ui: { order: 11, name: 'UI Component Libraries' },
  utils: { order: 12, name: 'Utility Libraries' },
};

/**
 * Recursively find all project.json files in the libs directory
 */
function findProjectFiles(dir) {
  const projectFiles = [];

  function traverse(currentPath) {
    const entries = fs.readdirSync(currentPath, { withFileTypes: true });

    for (const entry of entries) {
      const fullPath = path.join(currentPath, entry.name);

      if (entry.isDirectory()) {
        traverse(fullPath);
      } else if (entry.name === 'project.json') {
        projectFiles.push(fullPath);
      }
    }
  }

  traverse(dir);
  return projectFiles;
}

/**
 * Parse a project.json file and extract relevant metadata
 */
function parseProjectFile(filePath) {
  try {
    const content = fs.readFileSync(filePath, 'utf-8');
    const project = JSON.parse(content);

    // Extract library path relative to workspace root
    const relativePath = path.relative(path.join(__dirname, '..'), path.dirname(filePath));

    // Parse domain from path (e.g., libs/oms/feature/return-search -> oms)
    const pathParts = relativePath.split(path.sep);
    const domain = pathParts[1]; // First part after 'libs'

    // Try to extract description from README if available
    let description = project.description || '';
    const readmePath = path.join(path.dirname(filePath), 'README.md');
    if (!description && fs.existsSync(readmePath)) {
      const readme = fs.readFileSync(readmePath, 'utf-8');
      // Extract first paragraph or heading after title
      const match = readme.match(/^#[^\n]+\n+([^\n]+)/m);
      if (match) {
        description = match[1].replace(/^>?\s*/, '').trim();
      }
    }

    return {
      name: project.name,
      domain,
      path: relativePath,
      description,
      sourceRoot: project.sourceRoot,
    };
  } catch (error) {
    console.warn(`Warning: Failed to parse ${filePath}: ${error.message}`);
    return null;
  }
}

/**
 * Load path aliases from tsconfig.base.json
 */
function loadPathAliases() {
  try {
    const tsconfig = JSON.parse(fs.readFileSync(TSCONFIG_PATH, 'utf-8'));
    const paths = tsconfig.compilerOptions?.paths || {};
    const aliases = {};

    // Build a map from library path to path alias
    for (const [alias, pathArray] of Object.entries(paths)) {
      if (!alias.startsWith('@isa/')) continue;

      const libPath = pathArray[0]; // e.g., "libs/oms/feature/return-search/src/index.ts"
      if (libPath.startsWith('libs/')) {
        // Extract the library directory path
        const match = libPath.match(/^libs\/(.+?)\/src\//);
        if (match) {
          const libDir = match[1]; // e.g., "oms/feature/return-search"
          aliases[libDir] = alias;
        }
      }
    }

    return aliases;
  } catch (error) {
    console.warn('Warning: Failed to load path aliases from tsconfig.base.json');
    return {};
  }
}

/**
 * Get version information from package.json
 */
function getVersionInfo() {
  try {
    const packageJson = JSON.parse(fs.readFileSync(PACKAGE_JSON_PATH, 'utf-8'));
    const deps = packageJson.dependencies || {};
    const devDeps = packageJson.devDependencies || {};

    return {
      angular: deps['@angular/core']?.replace(/[\^~]/, '') || 'unknown',
      nx: devDeps['nx']?.replace(/[\^~]/, '') || 'unknown',
      node: packageJson.engines?.node?.replace(/>=\s*/, '≥') || 'unknown',
      npm: packageJson.engines?.npm?.replace(/>=\s*/, '≥') || 'unknown',
    };
  } catch (error) {
    console.warn('Warning: Failed to read version info from package.json');
    return {
      angular: 'unknown',
      nx: 'unknown',
      node: 'unknown',
      npm: 'unknown',
    };
  }
}

/**
 * Group libraries by domain
 */
function groupByDomain(libraries) {
  const grouped = {};

  for (const lib of libraries) {
    if (!lib) continue;

    if (!grouped[lib.domain]) {
      grouped[lib.domain] = [];
    }
    grouped[lib.domain].push(lib);
  }

  // Sort libraries within each domain by name
  for (const domain in grouped) {
    grouped[domain].sort((a, b) => a.name.localeCompare(b.name));
  }

  return grouped;
}

/**
 * Generate markdown content
 */
function generateMarkdown(librariesByDomain, totalCount, versions, pathAliases) {
  const lines = [];
  const now = new Date().toISOString().split('T')[0];

  // Header
  lines.push('# Library Reference Guide');
  lines.push('');
  lines.push(`> **Last Updated:** ${now}`);
  lines.push(`> **Angular Version:** ${versions.angular}`);
  lines.push(`> **Nx Version:** ${versions.nx}`);
  lines.push(`> **Total Libraries:** ${totalCount}`);
  lines.push('');
  lines.push(`All ${totalCount} libraries in the monorepo have comprehensive README.md documentation located at \`libs/[domain]/[layer]/[feature]/README.md\`.`);
  lines.push('');
  lines.push('**IMPORTANT: Always use the `docs-researcher` subagent** to retrieve and analyze library documentation. This keeps the main context clean and prevents pollution.');
  lines.push('');
  lines.push('---');
  lines.push('');

  // Get sorted domains
  const sortedDomains = Object.keys(librariesByDomain).sort((a, b) => {
    const orderA = DOMAIN_CONFIG[a]?.order || 999;
    const orderB = DOMAIN_CONFIG[b]?.order || 999;
    return orderA - orderB;
  });

  // Generate content for each domain
  for (const domain of sortedDomains) {
    const libraries = librariesByDomain[domain];
    const domainName = DOMAIN_CONFIG[domain]?.name || `${domain.charAt(0).toUpperCase() + domain.slice(1)} Domain`;

    lines.push(`## ${domainName} (${libraries.length} ${libraries.length === 1 ? 'library' : 'libraries'})`);
    lines.push('');

    for (const lib of libraries) {
      // Get path alias from tsconfig.base.json mappings
      // Extract relative lib path (e.g., "libs/oms/feature/return-search" -> "oms/feature/return-search")
      const relativeLibPath = lib.path.replace(/^libs\//, '');
      const pathAlias = pathAliases[relativeLibPath] || `@isa/${lib.name}`;

      lines.push(`### \`${pathAlias}\``);
      if (lib.description) {
        lines.push(lib.description);
        lines.push('');
      }
      lines.push(`**Location:** \`${lib.path}/\``);
      lines.push('');
    }

    lines.push('---');
    lines.push('');
  }

  // Footer
  lines.push('## How to Use This Guide');
  lines.push('');
  lines.push('1. **Quick Lookup**: Use this guide to find the purpose of any library in the monorepo');
  lines.push('2. **Detailed Documentation**: Always use the `docs-researcher` subagent to read the full README.md for implementation details');
  lines.push('3. **Path Resolution**: Use the location information to navigate to the library source code');
  lines.push('4. **Architecture Understanding**: Use `npx nx graph --filter=[library-name]` to visualize dependencies');
  lines.push('');
  lines.push('---');
  lines.push('');
  lines.push('## Maintenance Notes');
  lines.push('');
  lines.push('This file should be updated when:');
  lines.push('- New libraries are added to the monorepo');
  lines.push('- Libraries are renamed or moved');
  lines.push('- Library purposes significantly change');
  lines.push('- Angular or Nx versions are upgraded');
  lines.push('');
  lines.push('**Automation:** This file is auto-generated using `npm run docs:generate`. Run this command after adding or modifying libraries to keep the documentation up-to-date.');

  return lines.join('\n');
}

/**
 * Main execution
 */
function main() {
  console.log('🔍 Scanning for libraries in libs/ directory...');

  // Find all project.json files
  const projectFiles = findProjectFiles(LIBS_DIR);
  console.log(`   Found ${projectFiles.length} project.json files`);

  // Parse all project files
  const libraries = projectFiles.map(parseProjectFile).filter(Boolean);
  console.log(`   Parsed ${libraries.length} valid libraries`);

  // Load path aliases from tsconfig
  console.log('📖 Loading path aliases from tsconfig.base.json...');
  const pathAliases = loadPathAliases();
  console.log(`   Loaded ${Object.keys(pathAliases).length} path aliases`);

  // Group by domain
  const librariesByDomain = groupByDomain(libraries);
  const domainCount = Object.keys(librariesByDomain).length;
  console.log(`   Organized into ${domainCount} domains`);

  // Get version info
  const versions = getVersionInfo();

  // Generate markdown
  console.log('📝 Generating markdown content...');
  const markdown = generateMarkdown(librariesByDomain, libraries.length, versions, pathAliases);

  // Write to file
  console.log(`💾 Writing to ${OUTPUT_FILE}...`);
  fs.writeFileSync(OUTPUT_FILE, markdown, 'utf-8');

  // Summary
  console.log('');
  console.log('✅ Library reference documentation generated successfully!');
  console.log('');
  console.log('📊 Summary:');
  console.log(`   Total Libraries: ${libraries.length}`);
  console.log(`   Domains: ${domainCount}`);
  console.log(`   Output: ${path.relative(process.cwd(), OUTPUT_FILE)}`);
  console.log('');

  // Show domain breakdown
  console.log('📚 Libraries by Domain:');
  const sortedDomains = Object.keys(librariesByDomain).sort((a, b) => {
    const orderA = DOMAIN_CONFIG[a]?.order || 999;
    const orderB = DOMAIN_CONFIG[b]?.order || 999;
    return orderA - orderB;
  });

  for (const domain of sortedDomains) {
    const count = librariesByDomain[domain].length;
    const domainName = DOMAIN_CONFIG[domain]?.name || domain;
    console.log(`   ${domainName}: ${count}`);
  }
}

// Run if executed directly
if (require.main === module) {
  try {
    main();
  } catch (error) {
    console.error('❌ Error:', error.message);
    process.exit(1);
  }
}

module.exports = { findProjectFiles, parseProjectFile, groupByDomain, generateMarkdown };
