/**
 * Version Discovery Utility
 * Discovers actual versions of dependencies from multiple sources:
 * - Node.js: package.json
 * - Python: requirements.txt, pyproject.toml, Pipfile
 * - Go: go.mod
 * - Rust: Cargo.toml
 * - PHP: composer.json
 * Enables version-aware documentation generation
 */

import * as fs from 'fs';
import * as path from 'path';

export interface VersionMap {
  dependencies: Record<string, string>;
  devDependencies: Record<string, string>;
}

export type DependencySource = 'package.json' | 'requirements.txt' | 'pyproject.toml' | 'Pipfile' | 'go.mod' | 'Cargo.toml' | 'composer.json';

export interface ComponentVersions {
  frontend?: VersionMap;
  backend?: VersionMap;
  [componentName: string]: VersionMap | undefined;
}

/**
 * Discover versions from all dependency files in the output directory
 * Supports multiple languages and package managers
 */
export async function discoverVersions(outputDirectory: string): Promise<ComponentVersions> {
  const versions: ComponentVersions = {};

  // Define component locations and their possible dependency files
  const components = [
    {
      name: 'frontend',
      paths: [
        'frontend/package.json',
        'frontend/requirements.txt',
        'frontend/pyproject.toml',
      ]
    },
    {
      name: 'backend',
      paths: [
        'backend/package.json',
        'backend/requirements.txt',
        'backend/pyproject.toml',
        'backend/Pipfile',
        'backend/go.mod',
        'backend/Cargo.toml',
        'backend/composer.json',
      ]
    },
    {
      name: 'root',
      paths: [
        'package.json',
        'requirements.txt',
        'go.mod',
        'Cargo.toml',
      ]
    },
  ];

  for (const component of components) {
    // Try each possible dependency file
    for (const relativePath of component.paths) {
      const fullPath = path.join(outputDirectory, relativePath);

      if (fs.existsSync(fullPath)) {
        try {
          const versionMap = await parseVersionFile(fullPath);
          if (versionMap) {
            // Merge with existing versions if already found
            if (versions[component.name]) {
              versions[component.name] = {
                dependencies: {
                  ...versions[component.name]!.dependencies,
                  ...versionMap.dependencies,
                },
                devDependencies: {
                  ...versions[component.name]!.devDependencies,
                  ...versionMap.devDependencies,
                },
              };
            } else {
              versions[component.name] = versionMap;
            }
          }
        } catch (error) {
          console.warn(`Failed to parse ${fullPath}:`, error);
        }
      }
    }
  }

  return versions;
}

/**
 * Parse a dependency file and extract versions
 */
async function parseVersionFile(filePath: string): Promise<VersionMap | null> {
  const filename = path.basename(filePath);

  switch (filename) {
    case 'package.json':
      return parsePackageJson(filePath);
    case 'requirements.txt':
      return parseRequirementsTxt(filePath);
    case 'pyproject.toml':
      return parsePyprojectToml(filePath);
    case 'Pipfile':
      return parsePipfile(filePath);
    case 'go.mod':
      return parseGoMod(filePath);
    case 'Cargo.toml':
      return parseCargoToml(filePath);
    case 'composer.json':
      return parseComposerJson(filePath);
    default:
      return null;
  }
}

/**
 * Parse package.json (Node.js/npm)
 */
function parsePackageJson(filePath: string): VersionMap {
  const content = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
  return {
    dependencies: content.dependencies || {},
    devDependencies: content.devDependencies || {},
  };
}

/**
 * Parse requirements.txt (Python/pip)
 * Format: package==1.2.3 or package>=1.2.3
 */
function parseRequirementsTxt(filePath: string): VersionMap {
  const content = fs.readFileSync(filePath, 'utf-8');
  const dependencies: Record<string, string> = {};

  for (const line of content.split('\n')) {
    const trimmed = line.trim();

    // Skip comments and empty lines
    if (!trimmed || trimmed.startsWith('#')) continue;

    // Parse package==version or package>=version
    const match = trimmed.match(/^([a-zA-Z0-9_-]+)\s*([=><~!]+)\s*([0-9.]+)/);
    if (match) {
      const [, packageName, operator, version] = match;
      dependencies[packageName] = `${operator}${version}`;
    }
  }

  return { dependencies, devDependencies: {} };
}

/**
 * Parse pyproject.toml (Python/Poetry)
 * Simple TOML parser for dependencies section
 */
function parsePyprojectToml(filePath: string): VersionMap {
  const content = fs.readFileSync(filePath, 'utf-8');
  const dependencies: Record<string, string> = {};
  const devDependencies: Record<string, string> = {};

  // Simple regex-based parsing (not full TOML parser)
  const depSection = content.match(/\[tool\.poetry\.dependencies\]([\s\S]*?)(\[|$)/);
  const devDepSection = content.match(/\[tool\.poetry\.dev-dependencies\]([\s\S]*?)(\[|$)/);

  if (depSection) {
    parsePyprojectDeps(depSection[1], dependencies);
  }

  if (devDepSection) {
    parsePyprojectDeps(devDepSection[1], devDependencies);
  }

  return { dependencies, devDependencies };
}

function parsePyprojectDeps(section: string, target: Record<string, string>): void {
  const lines = section.split('\n');
  for (const line of lines) {
    const match = line.match(/^([a-zA-Z0-9_-]+)\s*=\s*"([^"]+)"/);
    if (match) {
      const [, packageName, version] = match;
      if (packageName !== 'python') {
        target[packageName] = version;
      }
    }
  }
}

/**
 * Parse Pipfile (Python/Pipenv)
 */
function parsePipfile(filePath: string): VersionMap {
  const content = fs.readFileSync(filePath, 'utf-8');
  const dependencies: Record<string, string> = {};
  const devDependencies: Record<string, string> = {};

  const depSection = content.match(/\[packages\]([\s\S]*?)(\[|$)/);
  const devDepSection = content.match(/\[dev-packages\]([\s\S]*?)(\[|$)/);

  if (depSection) {
    parsePipfileDeps(depSection[1], dependencies);
  }

  if (devDepSection) {
    parsePipfileDeps(devDepSection[1], devDependencies);
  }

  return { dependencies, devDependencies };
}

function parsePipfileDeps(section: string, target: Record<string, string>): void {
  const lines = section.split('\n');
  for (const line of lines) {
    const match = line.match(/^([a-zA-Z0-9_-]+)\s*=\s*"([^"]+)"/);
    if (match) {
      const [, packageName, version] = match;
      target[packageName] = version;
    }
  }
}

/**
 * Parse go.mod (Go modules)
 * Format: require package v1.2.3
 */
function parseGoMod(filePath: string): VersionMap {
  const content = fs.readFileSync(filePath, 'utf-8');
  const dependencies: Record<string, string> = {};

  // Match require blocks
  const requireBlock = content.match(/require\s*\(([\s\S]*?)\)/);
  if (requireBlock) {
    const lines = requireBlock[1].split('\n');
    for (const line of lines) {
      const match = line.trim().match(/^([^\s]+)\s+v([0-9.]+)/);
      if (match) {
        const [, packageName, version] = match;
        dependencies[packageName] = version;
      }
    }
  }

  // Match single require statements
  const singleRequires = content.matchAll(/require\s+([^\s]+)\s+v([0-9.]+)/g);
  for (const match of singleRequires) {
    const [, packageName, version] = match;
    dependencies[packageName] = version;
  }

  return { dependencies, devDependencies: {} };
}

/**
 * Parse Cargo.toml (Rust/Cargo)
 */
function parseCargoToml(filePath: string): VersionMap {
  const content = fs.readFileSync(filePath, 'utf-8');
  const dependencies: Record<string, string> = {};
  const devDependencies: Record<string, string> = {};

  const depSection = content.match(/\[dependencies\]([\s\S]*?)(\[|$)/);
  const devDepSection = content.match(/\[dev-dependencies\]([\s\S]*?)(\[|$)/);

  if (depSection) {
    parseCargoDeps(depSection[1], dependencies);
  }

  if (devDepSection) {
    parseCargoDeps(devDepSection[1], devDependencies);
  }

  return { dependencies, devDependencies };
}

function parseCargoDeps(section: string, target: Record<string, string>): void {
  const lines = section.split('\n');
  for (const line of lines) {
    // Simple format: package = "1.2.3"
    const simpleMatch = line.match(/^([a-zA-Z0-9_-]+)\s*=\s*"([^"]+)"/);
    if (simpleMatch) {
      const [, packageName, version] = simpleMatch;
      target[packageName] = version;
      continue;
    }

    // Object format: package = { version = "1.2.3" }
    const objMatch = line.match(/^([a-zA-Z0-9_-]+)\s*=\s*\{[^}]*version\s*=\s*"([^"]+)"/);
    if (objMatch) {
      const [, packageName, version] = objMatch;
      target[packageName] = version;
    }
  }
}

/**
 * Parse composer.json (PHP/Composer)
 */
function parseComposerJson(filePath: string): VersionMap {
  const content = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
  return {
    dependencies: content.require || {},
    devDependencies: content['require-dev'] || {},
  };
}

/**
 * Get version of a specific package from discovered versions
 */
export function getPackageVersion(
  versions: ComponentVersions,
  packageName: string,
  component?: string
): string | undefined {
  // If component specified, search in that component only
  if (component && versions[component]) {
    const versionMap = versions[component];
    return versionMap!.dependencies[packageName] || versionMap!.devDependencies[packageName];
  }

  // Otherwise, search all components
  for (const versionMap of Object.values(versions)) {
    if (!versionMap) continue;

    const version = versionMap.dependencies[packageName] || versionMap.devDependencies[packageName];
    if (version) {
      return version;
    }
  }

  return undefined;
}

/**
 * Clean version string (remove ^ ~ >= etc.)
 */
export function cleanVersion(version: string): string {
  return version.replace(/^[\^~>=<]+/, '');
}

/**
 * Get major version number
 */
export function getMajorVersion(version: string): number | undefined {
  const cleaned = cleanVersion(version);
  const match = cleaned.match(/^(\d+)/);
  return match ? parseInt(match[1], 10) : undefined;
}

/**
 * Get semantic version parts
 */
export function parseVersion(version: string): { major: number; minor: number; patch: number } | undefined {
  const cleaned = cleanVersion(version);
  const match = cleaned.match(/^(\d+)\.(\d+)\.(\d+)/);

  if (!match) return undefined;

  return {
    major: parseInt(match[1], 10),
    minor: parseInt(match[2], 10),
    patch: parseInt(match[3], 10),
  };
}

/**
 * Check if a version satisfies a requirement
 */
export function satisfiesVersion(
  actual: string,
  required: string
): boolean {
  const actualParsed = parseVersion(actual);
  const requiredParsed = parseVersion(required);

  if (!actualParsed || !requiredParsed) return false;

  // Simple major.minor.patch comparison
  if (actualParsed.major !== requiredParsed.major) {
    return actualParsed.major > requiredParsed.major;
  }

  if (actualParsed.minor !== requiredParsed.minor) {
    return actualParsed.minor > requiredParsed.minor;
  }

  return actualParsed.patch >= requiredParsed.patch;
}

/**
 * Discover technology versions based on common package patterns
 * Supports multiple languages and frameworks
 */
export async function discoverTechnologyVersions(
  outputDirectory: string
): Promise<{
  // Frontend frameworks
  react?: string;
  vue?: string;
  angular?: string;
  svelte?: string;

  // Backend frameworks
  express?: string;
  fastify?: string;
  nestjs?: string;
  flask?: string;
  django?: string;
  fastapi?: string;
  gin?: string;
  actix?: string;

  // ORMs & Database clients
  prisma?: string;
  typeorm?: string;
  drizzle?: string;
  sqlalchemy?: string;

  // Auth providers
  clerk?: string;
  auth0?: string;

  // Languages
  typescript?: string;
  python?: string;
  go?: string;
  rust?: string;

  [key: string]: string | undefined;
}> {
  const versions = await discoverVersions(outputDirectory);

  return {
    // Frontend
    react: getPackageVersion(versions, 'react'),
    vue: getPackageVersion(versions, 'vue'),
    angular: getPackageVersion(versions, '@angular/core'),
    svelte: getPackageVersion(versions, 'svelte'),

    // Backend - Node.js
    express: getPackageVersion(versions, 'express'),
    fastify: getPackageVersion(versions, 'fastify'),
    nestjs: getPackageVersion(versions, '@nestjs/core'),

    // Backend - Python
    flask: getPackageVersion(versions, 'Flask') || getPackageVersion(versions, 'flask'),
    django: getPackageVersion(versions, 'Django') || getPackageVersion(versions, 'django'),
    fastapi: getPackageVersion(versions, 'fastapi'),

    // Backend - Go
    gin: getPackageVersion(versions, 'github.com/gin-gonic/gin'),

    // Backend - Rust
    actix: getPackageVersion(versions, 'actix-web'),

    // ORMs
    prisma: getPackageVersion(versions, '@prisma/client'),
    typeorm: getPackageVersion(versions, 'typeorm'),
    drizzle: getPackageVersion(versions, 'drizzle-orm'),
    sqlalchemy: getPackageVersion(versions, 'SQLAlchemy') || getPackageVersion(versions, 'sqlalchemy'),

    // Auth
    clerk: getPackageVersion(versions, '@clerk/express') || getPackageVersion(versions, '@clerk/clerk-react'),
    auth0: getPackageVersion(versions, 'express-oauth2-jwt-bearer'),

    // Languages
    typescript: getPackageVersion(versions, 'typescript'),
    python: getPackageVersion(versions, 'python'),  // From pyproject.toml
    go: undefined,  // Go version usually in go.mod but not as a dependency
    rust: undefined,  // Rust version not in Cargo.toml
  };
}
