#!/usr/bin/env bun
/**
 * Lock files transform - handles lock file conflicts by accepting ours and regenerating
 *
 * Lock files (bun.lock, package-lock.json, yarn.lock, Cargo.lock, etc.) should not be
 * manually merged. Instead, we accept our version to resolve the conflict, then regenerate
 * the lock file fresh after the merge is complete.
 */

import { $ } from "bun"
import { info, success, warn, debug } from "../utils/logger"
import { defaultConfig } from "../utils/config"
import { checkoutOurs, stageFiles, getConflictedFiles } from "../utils/git"

export interface LockFileResult {
  file: string
  action: "resolved" | "skipped" | "regenerated" | "failed"
  dryRun: boolean
}

export interface LockFileOptions {
  dryRun?: boolean
  verbose?: boolean
  patterns?: string[]
}

/**
 * Check if a file is a lock file based on patterns
 */
export function isLockFile(path: string, patterns?: string[]): boolean {
  const lockPatterns = patterns || defaultConfig.lockFiles
  return lockPatterns.some((pattern) => {
    // Exact match
    if (path === pattern) return true

    // Glob pattern with **
    if (pattern.includes("**")) {
      const regex = new RegExp("^" + pattern.replace(/\*\*/g, ".*").replace(/\./g, "\\.") + "$")
      return regex.test(path)
    }

    // Simple glob pattern
    if (pattern.includes("*")) {
      const regex = new RegExp("^" + pattern.replace(/\./g, "\\.").replace(/\*/g, "[^/]*") + "$")
      return regex.test(path)
    }

    // Basename match (e.g., "bun.lock" matches "packages/foo/bun.lock")
    const basename = path.split("/").pop()
    return basename === pattern
  })
}

/**
 * Resolve lock file conflicts by accepting our version
 */
export async function resolveLockFileConflicts(options: LockFileOptions = {}): Promise<LockFileResult[]> {
  const results: LockFileResult[] = []
  const patterns = options.patterns || defaultConfig.lockFiles

  const conflicted = await getConflictedFiles()

  if (conflicted.length === 0) {
    debug("No conflicted files found")
    return results
  }

  const lockFiles = conflicted.filter((file) => isLockFile(file, patterns))

  if (lockFiles.length === 0) {
    debug("No lock file conflicts found")
    return results
  }

  info(`Found ${lockFiles.length} conflicted lock file(s)`)

  for (const file of lockFiles) {
    if (options.dryRun) {
      info(`[DRY-RUN] Would resolve conflict (accept ours): ${file}`)
      results.push({ file, action: "resolved", dryRun: true })
      continue
    }

    try {
      await checkoutOurs([file])
      await stageFiles([file])
      success(`Resolved lock file conflict (accepted ours): ${file}`)
      results.push({ file, action: "resolved", dryRun: false })
    } catch (err) {
      warn(`Failed to resolve lock file conflict: ${file} - ${err}`)
      results.push({ file, action: "failed", dryRun: false })
    }
  }

  return results
}

/**
 * Regenerate lock files after merge
 */
export async function regenerateLockFiles(options: LockFileOptions = {}): Promise<LockFileResult[]> {
  const results: LockFileResult[] = []

  // Check if bun.lock exists or was part of the merge
  const hasBunLock = await Bun.file("bun.lock").exists()

  if (hasBunLock) {
    if (options.dryRun) {
      info("[DRY-RUN] Would regenerate bun.lock via 'bun install'")
      results.push({ file: "bun.lock", action: "regenerated", dryRun: true })
    } else {
      info("Regenerating bun.lock...")
      const result = await $`bun install`.quiet().nothrow()
      if (result.exitCode === 0) {
        success("Regenerated bun.lock")
        results.push({ file: "bun.lock", action: "regenerated", dryRun: false })
      } else {
        warn(`Failed to regenerate bun.lock: ${result.stderr.toString()}`)
        results.push({ file: "bun.lock", action: "failed", dryRun: false })
      }
    }
  }

  // Note about nix/hashes.json - regenerated by CI, not locally
  const nixHashesPath = "nix/hashes.json"
  const hasNixHashes = await Bun.file(nixHashesPath).exists()

  if (hasNixHashes) {
    info("Note: nix/hashes.json will be regenerated by CI (update-nix-hashes.yml) after PR is created")
    results.push({ file: nixHashesPath, action: "skipped", dryRun: options.dryRun ?? false })
  }

  return results
}

// CLI entry point
if (import.meta.main) {
  const args = process.argv.slice(2)
  const dryRun = args.includes("--dry-run")
  const verbose = args.includes("--verbose")
  const regenerate = args.includes("--regenerate")

  if (dryRun) {
    info("Running in dry-run mode (no files will be modified)")
  }

  if (regenerate) {
    const results = await regenerateLockFiles({ dryRun, verbose })
    const regenerated = results.filter((r) => r.action === "regenerated")
    console.log()
    success(`Regenerated ${regenerated.length} lock file(s)`)
  } else {
    const results = await resolveLockFileConflicts({ dryRun, verbose })
    const resolved = results.filter((r) => r.action === "resolved")
    console.log()
    success(`Resolved ${resolved.length} lock file conflict(s)`)
  }

  if (dryRun) {
    info("Run without --dry-run to apply changes")
  }
}
