/**
 * @fileoverview Central version management module for DeepSource MCP Server
 *
 * This module provides a single source of truth for version information,
 * reading from package.json at build time with fallback support for runtime.
 */

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

/**
 * Interface representing parsed version information
 */
export interface VersionInfo {
  /** Full version string (e.g., "1.5.0") */
  version: string;
  /** Major version number */
  major: number;
  /** Minor version number */
  minor: number;
  /** Patch version number */
  patch: number;
  /** Pre-release version (e.g., "beta.1") */
  prerelease?: string;
  /** Build metadata (e.g., "20240101") */
  build?: string;
}

/**
 * Get the directory name for the current module
 * Works with ES modules
 */
function getCurrentDir(): string {
  try {
    const __filename = fileURLToPath(import.meta.url);
    return dirname(__filename);
  } catch {
    // Fallback for environments where import.meta.url is not available
    return process.cwd();
  }
}

/**
 * Read version from package.json
 * @returns The version string from package.json or null if unable to read
 */
function readVersionFromPackageJson(): string | null {
  try {
    const currentDir = getCurrentDir();
    // Try multiple paths to find package.json
    const possiblePaths = [
      join(currentDir, '../../package.json'), // From src/version.ts
      join(currentDir, '../package.json'), // From dist/version.js
      join(process.cwd(), 'package.json'), // From current working directory
    ];

    for (const packagePath of possiblePaths) {
      try {
        const packageContent = readFileSync(packagePath, 'utf-8');
        const packageJson = JSON.parse(packageContent) as { version?: string };
        if (packageJson.version) {
          return packageJson.version;
        }
      } catch {
        // Try next path
        continue;
      }
    }
    return null;
  } catch {
    return null;
  }
}

/**
 * Parse a semver version string into components
 * @param versionString - The version string to parse
 * @returns Parsed version information or null if invalid
 */
export function parseVersion(versionString: string): VersionInfo | null {
  // Regex for semantic versioning
  // Matches: major.minor.patch[-prerelease][+build]
  const semverRegex = /^(\d+)\.(\d+)\.(\d+)(?:-([a-zA-Z0-9.-]+))?(?:\+([a-zA-Z0-9.-]+))?$/;
  const match = versionString.match(semverRegex);

  if (!match) {
    return null;
  }

  const [, majorStr, minorStr, patchStr, prerelease, build] = match;

  // These are guaranteed to be strings when the regex matches
  // The regex requires \d+ for major, minor, and patch
  if (!majorStr || !minorStr || !patchStr) {
    // This should never happen due to regex, but satisfies TypeScript
    return null;
  }

  return {
    version: versionString,
    major: parseInt(majorStr, 10),
    minor: parseInt(minorStr, 10),
    patch: parseInt(patchStr, 10),
    ...(prerelease && { prerelease }),
    ...(build && { build }),
  };
}

/**
 * Validate if a string is a valid semver version
 * @param versionString - The version string to validate
 * @returns True if valid semver format
 */
export function validateVersion(versionString: string): boolean {
  return parseVersion(versionString) !== null;
}

/**
 * Compare two semver versions
 * @param v1 - First version
 * @param v2 - Second version
 * @returns -1 if v1 < v2, 0 if v1 === v2, 1 if v1 > v2
 */
export function compareVersions(v1: string, v2: string): number {
  const version1 = parseVersion(v1);
  const version2 = parseVersion(v2);

  if (!version1 || !version2) {
    throw new Error('Invalid version format');
  }

  // Compare major
  if (version1.major !== version2.major) {
    return version1.major < version2.major ? -1 : 1;
  }

  // Compare minor
  if (version1.minor !== version2.minor) {
    return version1.minor < version2.minor ? -1 : 1;
  }

  // Compare patch
  if (version1.patch !== version2.patch) {
    return version1.patch < version2.patch ? -1 : 1;
  }

  // Compare prerelease (absence means higher version)
  if (version1.prerelease && !version2.prerelease) {
    return -1;
  }
  if (!version1.prerelease && version2.prerelease) {
    return 1;
  }
  if (version1.prerelease && version2.prerelease) {
    return version1.prerelease.localeCompare(version2.prerelease);
  }

  return 0;
}

// Initialize version - try to read from package.json, fallback to build-time constant
function determineVersion(): string {
  const packageVersion = readVersionFromPackageJson();
  if (packageVersion && validateVersion(packageVersion)) {
    return packageVersion;
  }

  // This will be replaced during build process
  const buildVersion = '__BUILD_VERSION__';

  // If still the placeholder, use a fallback version
  if (buildVersion === '__BUILD_VERSION__' || !validateVersion(buildVersion)) {
    return '0.0.0-dev';
  }

  return buildVersion;
}

const VERSION = determineVersion();

/**
 * The current version of the DeepSource MCP Server
 * @constant
 */
export { VERSION };

/**
 * Get the current version of the DeepSource MCP Server
 * @returns The current version string
 */
export function getVersion(): string {
  return VERSION;
}

/**
 * Get detailed version information
 * @returns Parsed version information
 */
export function getVersionInfo(): VersionInfo {
  const info = parseVersion(VERSION);
  if (!info) {
    // Should never happen due to validation above, but TypeScript needs this
    return {
      version: VERSION,
      major: 0,
      minor: 0,
      patch: 0,
    };
  }
  return info;
}

/**
 * Get a formatted version string for display
 * @param includePrerelease - Whether to include prerelease info
 * @param includeBuild - Whether to include build metadata
 * @returns Formatted version string
 */
export function getFormattedVersion(includePrerelease = true, includeBuild = false): string {
  const info = getVersionInfo();
  let formatted = `${info.major}.${info.minor}.${info.patch}`;

  if (includePrerelease && info.prerelease) {
    formatted += `-${info.prerelease}`;
  }

  if (includeBuild && info.build) {
    formatted += `+${info.build}`;
  }

  return formatted;
}
