/**
 * GitFlow Version — Detection, SemVer bumping, source file updates.
 * Self-contained: imports only from local lib + node built-ins.
 */

import { readFile, writeFile } from 'fs/promises';
import { existsSync, readdirSync, statSync } from 'fs';
import { join } from 'path';
import { execGit } from './git.js';
import type { VersionInfo } from './types.js';

export async function detectVersion(projectDir: string): Promise<VersionInfo> {
  const csprojVersion = await findCsprojVersion(projectDir);
  if (csprojVersion) return csprojVersion;

  const buildPropsPath = join(projectDir, 'Directory.Build.props');
  if (existsSync(buildPropsPath)) {
    const content = await readFile(buildPropsPath, 'utf-8');
    const match = content.match(/<Version>([^<]+)<\/Version>/);
    if (match) return { current: match[1], source: 'Directory.Build.props', file: buildPropsPath };
  }

  const pkgPath = join(projectDir, 'package.json');
  if (existsSync(pkgPath)) {
    try {
      const pkg = JSON.parse(await readFile(pkgPath, 'utf-8'));
      if (pkg.version) return { current: pkg.version, source: 'package.json', file: pkgPath };
    } catch { /* skip */ }
  }

  const versionFile = join(projectDir, 'VERSION');
  if (existsSync(versionFile)) {
    const content = (await readFile(versionFile, 'utf-8')).trim();
    if (/^\d+\.\d+\.\d+/.test(content)) return { current: content, source: 'VERSION', file: versionFile };
  }

  const tagResult = await execGit(['describe', '--tags', '--abbrev=0'], projectDir);
  if (tagResult.exitCode === 0) {
    const version = tagResult.stdout.replace(/^v/, '');
    if (/^\d+\.\d+\.\d+/.test(version)) return { current: version, source: 'tag' };
  }

  return { current: '0.0.0', source: 'default' };
}

export function bumpVersion(current: string, type: 'major' | 'minor' | 'patch'): string {
  const parts = current.replace(/^v/, '').split('.').map(Number);
  const [major = 0, minor = 0, patch = 0] = parts;

  switch (type) {
    case 'major': return `${major + 1}.0.0`;
    case 'minor': return `${major}.${minor + 1}.0`;
    case 'patch': return `${major}.${minor}.${patch + 1}`;
  }
}

export function getBumpType(branchType: string): 'major' | 'minor' | 'patch' {
  switch (branchType) {
    case 'hotfix': return 'patch';
    case 'release': return 'minor';
    default: return 'minor';
  }
}

export async function updateVersionSources(
  projectDir: string,
  newVersion: string,
  sources?: string[],
): Promise<string[]> {
  const modified: string[] = [];
  const toCheck = sources || ['package.json', 'Directory.Build.props', 'VERSION'];

  for (const source of toCheck) {
    switch (source) {
      case 'package.json': {
        const pkgPath = join(projectDir, 'package.json');
        if (existsSync(pkgPath)) {
          const content = await readFile(pkgPath, 'utf-8');
          const pkg = JSON.parse(content);
          if (pkg.version !== newVersion) {
            pkg.version = newVersion;
            await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
            modified.push(pkgPath);
          }
        }
        break;
      }
      case 'Directory.Build.props': {
        const propsPath = join(projectDir, 'Directory.Build.props');
        if (existsSync(propsPath)) {
          const content = await readFile(propsPath, 'utf-8');
          const updated = content.replace(/<Version>[^<]+<\/Version>/, `<Version>${newVersion}</Version>`);
          if (updated !== content) {
            await writeFile(propsPath, updated);
            modified.push(propsPath);
          }
        }
        break;
      }
      case 'VERSION': {
        const vFile = join(projectDir, 'VERSION');
        if (existsSync(vFile)) {
          await writeFile(vFile, newVersion + '\n');
          modified.push(vFile);
        }
        break;
      }
      case 'csproj': {
        const csprojFiles = await findCsprojFiles(projectDir);
        for (const f of csprojFiles) {
          const content = await readFile(f, 'utf-8');
          const updated = content.replace(/<Version>[^<]+<\/Version>/, `<Version>${newVersion}</Version>`);
          if (updated !== content) {
            await writeFile(f, updated);
            modified.push(f);
          }
        }
        break;
      }
    }
  }

  return modified;
}

async function findCsprojVersion(projectDir: string): Promise<VersionInfo | null> {
  const files = await findCsprojFiles(projectDir);
  for (const f of files) {
    const content = await readFile(f, 'utf-8');
    const match = content.match(/<Version>([^<]+)<\/Version>/);
    if (match) return { current: match[1], source: 'csproj', file: f };
  }
  return null;
}

async function findCsprojFiles(projectDir: string): Promise<string[]> {
  const results: string[] = [];
  const srcDir = join(projectDir, 'src');
  if (!existsSync(srcDir)) return results;

  try {
    for (const folder of readdirSync(srcDir)) {
      const folderPath = join(srcDir, folder);
      if (!statSync(folderPath).isDirectory()) continue;
      for (const file of readdirSync(folderPath)) {
        if (file.endsWith('.csproj')) results.push(join(folderPath, file));
      }
    }
  } catch { /* skip */ }

  return results;
}
