import { readFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { pathExists, readJson, writeJson } from './file-copier.js';

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

/**
 * Get the current CLI version from package.json
 * @returns {string} Version string (e.g., "1.0.4")
 */
export function getInstalledCLIVersion() {
  try {
    const pkgPath = join(__dirname, '..', '..', 'package.json');
    const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
    return pkg.version;
  } catch (error) {
    return 'unknown';
  }
}

/**
 * Get the latest version from npm registry
 * @param {string} packageName - The package name (default: @polymorphism-tech/morph-spec)
 * @returns {Promise<string|null>} Latest version or null if failed
 */
export async function getLatestNPMVersion(packageName = '@polymorphism-tech/morph-spec') {
  try {
    const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`);
    if (!response.ok) return null;
    const data = await response.json();
    return data.version;
  } catch (error) {
    return null;
  }
}

/**
 * Get the MORPH version installed in the project
 * @param {string} projectPath - Path to the project
 * @returns {Promise<object|null>} Version info object or null if not found
 */
export async function getProjectMorphVersion(projectPath) {
  try {
    const versionPath = join(projectPath, '.morph', '.morphversion');
    if (!(await pathExists(versionPath))) {
      return null;
    }
    return await readJson(versionPath);
  } catch (error) {
    return null;
  }
}

/**
 * Save the MORPH version to the project
 * @param {string} projectPath - Path to the project
 * @param {string} version - Version to save
 * @returns {Promise<void>}
 */
export async function saveProjectMorphVersion(projectPath, version) {
  const versionPath = join(projectPath, '.morph', '.morphversion');
  const versionInfo = {
    morphVersion: version,
    installedAt: new Date().toISOString(),
    cliVersion: version
  };
  await writeJson(versionPath, versionInfo);
}

/**
 * Compare two semver versions
 * @param {string} v1 - First version
 * @param {string} v2 - Second version
 * @returns {number} -1 if v1 < v2, 0 if equal, 1 if v1 > v2
 */
export function compareVersions(v1, v2) {
  if (!v1 || !v2) return 0;

  const parts1 = v1.split('.').map(Number);
  const parts2 = v2.split('.').map(Number);

  for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
    const num1 = parts1[i] || 0;
    const num2 = parts2[i] || 0;

    if (num1 < num2) return -1;
    if (num1 > num2) return 1;
  }

  return 0;
}

/**
 * Detect how the CLI was installed
 * @returns {'global'|'npx'|'unknown'}
 */
export function detectInstallMethod() {
  // Check if running via npx
  if (process.env.npm_execpath && process.env.npm_execpath.includes('npx')) {
    return 'npx';
  }

  // Check if installed globally
  const isGlobal = process.env.npm_config_global === 'true' ||
                   process.execPath.includes('node_modules');

  if (isGlobal) {
    return 'global';
  }

  return 'unknown';
}

/**
 * Get update instructions based on install method
 * @param {string} method - Install method ('global', 'npx', 'unknown')
 * @returns {string[]} Array of instruction strings
 */
export function getUpdateInstructions(method) {
  switch (method) {
    case 'global':
      return [
        'npm install -g @polymorphism-tech/morph-spec@latest',
        'morph-spec update'
      ];
    case 'npx':
      return [
        'npx @polymorphism-tech/morph-spec@latest update'
      ];
    default:
      return [
        'npm install -g @polymorphism-tech/morph-spec@latest',
        'morph-spec update',
        '',
        'Or use npx:',
        'npx @polymorphism-tech/morph-spec@latest update'
      ];
  }
}

/**
 * Check if CLI version is outdated
 * @returns {Promise<{isOutdated: boolean, current: string, latest: string|null}>}
 */
export async function checkCLIOutdated() {
  const current = getInstalledCLIVersion();
  const latest = await getLatestNPMVersion();

  if (!latest) {
    return { isOutdated: false, current, latest: null };
  }

  const isOutdated = compareVersions(current, latest) < 0;

  return { isOutdated, current, latest };
}

/**
 * Check if project MORPH version is outdated
 * @param {string} projectPath - Path to the project
 * @returns {Promise<{isOutdated: boolean, current: string|null, cliVersion: string}>}
 */
export async function checkProjectOutdated(projectPath) {
  const cliVersion = getInstalledCLIVersion();
  const projectVersion = await getProjectMorphVersion(projectPath);

  if (!projectVersion) {
    return { isOutdated: false, current: null, cliVersion };
  }

  const isOutdated = compareVersions(projectVersion.morphVersion, cliVersion) < 0;

  return { isOutdated, current: projectVersion.morphVersion, cliVersion };
}
