import { execSync } from 'child_process';
import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';

interface Version {
  major: number;
  minor: number;
  patch: number;
}

function parseVersion(version: string): Version {
  const [major, minor, patch] = version.split('.').map(Number);
  return { major, minor, patch };
}

function formatVersion(v: Version): string {
  return `${v.major}.${v.minor}.${v.patch}`;
}

function getLastVersionTag(): string | null {
  try {
    const tag = execSync('git describe --tags --abbrev=0', {
      encoding: 'utf-8',
      stdio: ['ignore', 'pipe', 'ignore'],
    }).trim();
    return tag;
  } catch {
    return null;
  }
}

function getCommitsSinceTag(tag: string | null): string[] {
  try {
    const range = tag ? `${tag}..HEAD` : 'HEAD';
    const commits = execSync(`git log ${range} --pretty=format:"%s"`, {
      encoding: 'utf-8',
      stdio: ['ignore', 'pipe', 'ignore'],
    })
      .trim()
      .split('\n')
      .filter(Boolean);
    return commits;
  } catch {
    return [];
  }
}

function determineVersionBump(commits: string[]): 'major' | 'minor' | 'patch' {
  let hasBreaking = false;
  let hasFeature = false;
  let hasPatch = false;

  for (const commit of commits) {
    const lowerCommit = commit.toLowerCase();

    // Breaking changes (major bump)
    if (
      lowerCommit.includes('breaking change') ||
      lowerCommit.includes('breaking:') ||
      lowerCommit.match(/^[a-z]+!:/) || // feat!:, refactor!:, etc.
      lowerCommit.includes('!')
    ) {
      hasBreaking = true;
    }

    // Features (minor bump)
    if (lowerCommit.startsWith('feat:')) {
      hasFeature = true;
    }

    // Patch bumps: fixes, performance improvements, security fixes, refactorings
    if (
      lowerCommit.startsWith('fix:') ||
      lowerCommit.startsWith('perf:') ||
      lowerCommit.startsWith('security:') ||
      lowerCommit.startsWith('patch:') ||
      lowerCommit.startsWith('refactor:')
    ) {
      hasPatch = true;
    }
  }

  if (hasBreaking) return 'major';
  if (hasFeature) return 'minor';
  if (hasPatch) return 'patch';

  // Par défaut, patch si on a des commits mais aucun type spécifique
  return commits.length > 0 ? 'patch' : 'patch';
}

function incrementVersion(
  current: Version,
  type: 'major' | 'minor' | 'patch'
): Version {
  switch (type) {
    case 'major':
      return { major: current.major + 1, minor: 0, patch: 0 };
    case 'minor':
      return { major: current.major, minor: current.minor + 1, patch: 0 };
    case 'patch':
      return {
        major: current.major,
        minor: current.minor,
        patch: current.patch + 1,
      };
  }
}

function main() {
  const silent = process.argv.includes('--silent');
  const hookMode = process.argv.includes('--hook');

  try {
    const packagePath = join(process.cwd(), 'package.json');
    const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));
    const currentVersion = parseVersion(packageJson.version);

    const lastTag = getLastVersionTag();
    const commits = getCommitsSinceTag(lastTag);

    if (commits.length === 0) {
      if (!silent) {
        console.log('✅ Aucun nouveau commit depuis la dernière version');
        console.log(`Version actuelle: ${packageJson.version}`);
      }
      return;
    }

    const bumpType = determineVersionBump(commits);
    const newVersion = incrementVersion(currentVersion, bumpType);
    const newVersionString = formatVersion(newVersion);

    // Si la version n'a pas changé, ne rien faire
    if (newVersionString === packageJson.version) {
      return;
    }

    if (!silent) {
      console.log(`📊 Analyse des commits depuis ${lastTag || 'le début'}:`);
      console.log(`   - ${commits.length} commit(s) trouvé(s)`);
      console.log(`   - Type de bump détecté: ${bumpType}`);
      console.log(`   - Version actuelle: ${packageJson.version}`);
      console.log(`   - Nouvelle version: ${newVersionString}`);

      // Afficher les commits pertinents
      console.log('\n📝 Commits analysés:');
      commits.slice(0, 10).forEach((commit) => {
        console.log(`   - ${commit}`);
      });
      if (commits.length > 10) {
        console.log(`   ... et ${commits.length - 10} autre(s) commit(s)`);
      }
    }

    // Mettre à jour package.json
    packageJson.version = newVersionString;
    writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + '\n');

    if (!silent) {
      console.log(`\n✅ Version mise à jour dans package.json`);
      console.log(
        `\n💡 Prochaines étapes:` +
          `\n   1. git add package.json` +
          `\n   2. git commit -m "chore: bump version to ${newVersionString}"` +
          `\n   3. git tag v${newVersionString}`
      );
    } else if (hookMode) {
      // En mode hook, ajouter package.json au staging
      try {
        execSync('git add package.json', { stdio: 'ignore' });
      } catch {
        // Ignorer les erreurs en mode hook
      }
    }
  } catch (error) {
    if (!silent) {
      console.error('❌ Erreur lors de la mise à jour de version:', error);
    }
    if (!hookMode) {
      process.exit(1);
    }
  }
}

main();
