/**
 * NERF Worktree CLI — standalone subcommand and -w flag handling.
 *
 * Manages the full worktree lifecycle from the command line:
 *   nerf -w                    Create auto-named worktree, start interactive session
 *   nerf -w my-feature         Create/resume named worktree
 *   nerf worktree list         List worktrees with status
 *   nerf worktree merge [name] Squash-merge a worktree into main
 *   nerf worktree clean        Remove all merged/empty worktrees
 *   nerf worktree remove <n>   Remove a specific worktree
 *
 * On session exit (via session_shutdown event), auto-commits dirty work
 * so nothing is lost. The NERF extension reads NERF_CLI_WORKTREE to know
 * when a session was launched via -w.
 *
 * Note: Extension modules are .ts files loaded via jiti (not compiled to .js).
 * We use createJiti() here because this module is compiled by tsc but imports
 * from resources/extensions/nerf-engine/ which are shipped as raw .ts (#1283).
 */
import chalk from 'chalk';
import { createJiti } from '@mariozechner/jiti';
import { fileURLToPath } from 'node:url';
import { generateWorktreeName } from './worktree-name-gen.js';
import { existsSync } from 'node:fs';
import { resolveBundledSourceResource } from './bundled-resource-path.js';
const jiti = createJiti(fileURLToPath(import.meta.url), { interopDefault: true, debug: false });
const nerfExtensionPath = (...segments) => resolveBundledSourceResource(import.meta.url, 'extensions', 'nerf-engine', ...segments);
// Lazily-loaded extension modules (loaded once on first use via jiti)
let _ext = null;
async function loadExtensionModules() {
    if (_ext)
        return _ext;
    const [wtMgr, autoWt, gitBridge, gitSvc, wt] = await Promise.all([
        jiti.import(nerfExtensionPath('worktree-manager.ts'), {}),
        jiti.import(nerfExtensionPath('auto-worktree.ts'), {}),
        jiti.import(nerfExtensionPath('native-git-bridge.ts'), {}),
        jiti.import(nerfExtensionPath('git-service.ts'), {}),
        jiti.import(nerfExtensionPath('worktree.ts'), {}),
    ]);
    _ext = {
        createWorktree: wtMgr.createWorktree,
        listWorktrees: wtMgr.listWorktrees,
        removeWorktree: wtMgr.removeWorktree,
        mergeWorktreeToMain: wtMgr.mergeWorktreeToMain,
        diffWorktreeAll: wtMgr.diffWorktreeAll,
        diffWorktreeNumstat: wtMgr.diffWorktreeNumstat,
        worktreeBranchName: wtMgr.worktreeBranchName,
        worktreePath: wtMgr.worktreePath,
        runWorktreePostCreateHook: autoWt.runWorktreePostCreateHook,
        nativeHasChanges: gitBridge.nativeHasChanges,
        nativeDetectMainBranch: gitBridge.nativeDetectMainBranch,
        nativeCommitCountBetween: gitBridge.nativeCommitCountBetween,
        inferCommitType: gitSvc.inferCommitType,
        autoCommitCurrentBranch: wt.autoCommitCurrentBranch,
    };
    return _ext;
}
// ─── Status Helpers ─────────────────────────────────────────────────────────
function getWorktreeStatus(ext, basePath, name, wtPath) {
    const diff = ext.diffWorktreeAll(basePath, name);
    const numstat = ext.diffWorktreeNumstat(basePath, name);
    const filesChanged = diff.added.length + diff.modified.length + diff.removed.length;
    let linesAdded = 0;
    let linesRemoved = 0;
    for (const s of numstat) {
        linesAdded += s.added;
        linesRemoved += s.removed;
    }
    let uncommitted = false;
    try {
        uncommitted = existsSync(wtPath) && ext.nativeHasChanges(wtPath);
    }
    catch (err) {
        const code = err?.code ?? err?.message ?? "";
        if (code !== "ENOENT" && !String(code).includes("not a git")) {
            process.stderr.write(`[nerf] Warning: hasChanges failed for ${wtPath}: ${err instanceof Error ? err.message : String(err)}\n`);
        }
        // Leave uncommitted=false — ambiguous state is safer than assuming dirty
    }
    let commits = 0;
    try {
        const mainBranch = ext.nativeDetectMainBranch(basePath);
        commits = ext.nativeCommitCountBetween(basePath, mainBranch, ext.worktreeBranchName(name));
    }
    catch (err) {
        process.stderr.write(`[nerf] Warning: commitCountBetween failed for ${name}: ${err instanceof Error ? err.message : String(err)}\n`);
        // Leave commits=0 — do not auto-remove worktree based on ambiguous git state
    }
    return {
        name,
        path: wtPath,
        branch: ext.worktreeBranchName(name),
        exists: existsSync(wtPath),
        filesChanged,
        linesAdded,
        linesRemoved,
        uncommitted,
        commits,
    };
}
// ─── Formatters ─────────────────────────────────────────────────────────────
function formatStatus(s) {
    const lines = [];
    const badge = s.uncommitted
        ? chalk.yellow(' (uncommitted)')
        : s.filesChanged > 0
            ? chalk.cyan(' (unmerged)')
            : chalk.green(' (clean)');
    lines.push(`  ${chalk.bold.cyan(s.name)}${badge}`);
    lines.push(`    ${chalk.dim('branch')}  ${chalk.magenta(s.branch)}`);
    lines.push(`    ${chalk.dim('path')}    ${chalk.dim(s.path)}`);
    if (s.filesChanged > 0) {
        lines.push(`    ${chalk.dim('diff')}    ${s.filesChanged} files, ${chalk.green(`+${s.linesAdded}`)} ${chalk.red(`-${s.linesRemoved}`)}, ${s.commits} commit${s.commits === 1 ? '' : 's'}`);
    }
    return lines.join('\n');
}
// ─── Subcommand: list ───────────────────────────────────────────────────────
async function handleList(basePath) {
    const ext = await loadExtensionModules();
    const worktrees = ext.listWorktrees(basePath);
    if (worktrees.length === 0) {
        process.stderr.write(chalk.dim('No worktrees. Create one with: nerf -w <name>\n'));
        return;
    }
    process.stderr.write(chalk.bold('\nWorktrees\n\n'));
    for (const wt of worktrees) {
        const status = getWorktreeStatus(ext, basePath, wt.name, wt.path);
        process.stderr.write(formatStatus(status) + '\n\n');
    }
}
// ─── Subcommand: merge ──────────────────────────────────────────────────────
async function handleMerge(basePath, args) {
    const ext = await loadExtensionModules();
    const name = args[0];
    if (!name) {
        // If only one worktree exists, merge it
        const worktrees = ext.listWorktrees(basePath);
        if (worktrees.length === 1) {
            await doMerge(ext, basePath, worktrees[0].name);
            return;
        }
        process.stderr.write(chalk.red('Usage: nerf worktree merge <name>\n'));
        process.stderr.write(chalk.dim('Run nerf worktree list to see worktrees.\n'));
        process.exit(1);
    }
    await doMerge(ext, basePath, name);
}
async function doMerge(ext, basePath, name) {
    const worktrees = ext.listWorktrees(basePath);
    const wt = worktrees.find(w => w.name === name);
    if (!wt) {
        process.stderr.write(chalk.red(`Worktree "${name}" not found.\n`));
        process.exit(1);
    }
    const status = getWorktreeStatus(ext, basePath, name, wt.path);
    if (status.filesChanged === 0 && !status.uncommitted) {
        process.stderr.write(chalk.dim(`Worktree "${name}" has no changes to merge.\n`));
        // Clean up empty worktree
        ext.removeWorktree(basePath, name, { deleteBranch: true });
        process.stderr.write(chalk.green(`Removed empty worktree ${chalk.bold(name)}.\n`));
        return;
    }
    // Auto-commit dirty work before merge
    if (status.uncommitted) {
        try {
            ext.autoCommitCurrentBranch(wt.path, 'worktree-merge', name);
            process.stderr.write(chalk.dim('  Auto-committed dirty work before merge.\n'));
        }
        catch { /* best-effort */ }
    }
    const commitType = ext.inferCommitType(name);
    const commitMessage = `${commitType}: merge worktree ${name}\n\nNERF-Worktree: ${name}`;
    process.stderr.write(`\nMerging ${chalk.bold.cyan(name)} → ${chalk.magenta(ext.nativeDetectMainBranch(basePath))}\n`);
    process.stderr.write(chalk.dim(`  ${status.filesChanged} files, ${chalk.green(`+${status.linesAdded}`)} ${chalk.red(`-${status.linesRemoved}`)}\n\n`));
    try {
        ext.mergeWorktreeToMain(basePath, name, commitMessage);
        ext.removeWorktree(basePath, name, { deleteBranch: true });
        process.stderr.write(chalk.green(`✓ Merged and cleaned up ${chalk.bold(name)}\n`));
        process.stderr.write(chalk.dim(`  commit: ${commitMessage}\n`));
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        process.stderr.write(chalk.red(`✗ Merge failed: ${msg}\n`));
        process.stderr.write(chalk.dim('  Resolve conflicts manually, then run nerf worktree merge again.\n'));
        process.exit(1);
    }
}
// ─── Subcommand: clean ──────────────────────────────────────────────────────
async function handleClean(basePath) {
    const ext = await loadExtensionModules();
    const worktrees = ext.listWorktrees(basePath);
    if (worktrees.length === 0) {
        process.stderr.write(chalk.dim('No worktrees to clean.\n'));
        return;
    }
    let cleaned = 0;
    for (const wt of worktrees) {
        const status = getWorktreeStatus(ext, basePath, wt.name, wt.path);
        if (status.filesChanged === 0 && !status.uncommitted) {
            try {
                ext.removeWorktree(basePath, wt.name, { deleteBranch: true });
                process.stderr.write(chalk.green(`  ✓ Removed ${chalk.bold(wt.name)} (clean)\n`));
                cleaned++;
            }
            catch {
                process.stderr.write(chalk.yellow(`  ✗ Failed to remove ${wt.name}\n`));
            }
        }
        else {
            process.stderr.write(chalk.dim(`  ─ Kept ${chalk.bold(wt.name)} (${status.filesChanged} changed files)\n`));
        }
    }
    process.stderr.write(chalk.dim(`\nCleaned ${cleaned} worktree${cleaned === 1 ? '' : 's'}.\n`));
}
// ─── Subcommand: remove ─────────────────────────────────────────────────────
async function handleRemove(basePath, args) {
    const ext = await loadExtensionModules();
    const name = args[0];
    if (!name) {
        process.stderr.write(chalk.red('Usage: nerf worktree remove <name>\n'));
        process.exit(1);
    }
    const worktrees = ext.listWorktrees(basePath);
    const wt = worktrees.find(w => w.name === name);
    if (!wt) {
        process.stderr.write(chalk.red(`Worktree "${name}" not found.\n`));
        process.exit(1);
    }
    const status = getWorktreeStatus(ext, basePath, name, wt.path);
    if (status.filesChanged > 0 || status.uncommitted) {
        process.stderr.write(chalk.yellow(`⚠ Worktree "${name}" has unmerged changes (${status.filesChanged} files).\n`));
        process.stderr.write(chalk.yellow('  Use --force to remove anyway, or merge first: nerf worktree merge ' + name + '\n'));
        if (!process.argv.includes('--force')) {
            process.exit(1);
        }
    }
    ext.removeWorktree(basePath, name, { deleteBranch: true });
    process.stderr.write(chalk.green(`✓ Removed worktree ${chalk.bold(name)}\n`));
}
// ─── Subcommand: status (default when no args) ─────────────────────────────
async function handleStatusBanner(basePath) {
    const ext = await loadExtensionModules();
    const worktrees = ext.listWorktrees(basePath);
    if (worktrees.length === 0)
        return;
    const withChanges = worktrees.filter(wt => {
        try {
            const diff = ext.diffWorktreeAll(basePath, wt.name);
            return diff.added.length + diff.modified.length + diff.removed.length > 0;
        }
        catch {
            return false;
        }
    });
    if (withChanges.length === 0)
        return;
    const names = withChanges.map(w => chalk.cyan(w.name)).join(', ');
    process.stderr.write(chalk.dim('[nerf] ') +
        chalk.yellow(`${withChanges.length} worktree${withChanges.length === 1 ? '' : 's'} with unmerged changes: `) +
        names + '\n' +
        chalk.dim('[nerf] ') +
        chalk.dim('Resume: nerf -w <name>  |  Merge: nerf worktree merge <name>  |  List: nerf worktree list\n\n'));
}
// ─── -w flag: create/resume worktree for interactive session ────────────────
async function handleWorktreeFlag(worktreeFlag) {
    const ext = await loadExtensionModules();
    const basePath = process.cwd();
    // nerf -w (no name) — resume most recent worktree with changes, or create new
    if (worktreeFlag === true) {
        const existing = ext.listWorktrees(basePath);
        const withChanges = existing.filter(wt => {
            try {
                const diff = ext.diffWorktreeAll(basePath, wt.name);
                return diff.added.length + diff.modified.length + diff.removed.length > 0;
            }
            catch {
                return false;
            }
        });
        if (withChanges.length === 1) {
            // Single active worktree — resume it
            const wt = withChanges[0];
            process.chdir(wt.path);
            process.env.NERF_CLI_WORKTREE = wt.name;
            process.env.NERF_CLI_WORKTREE_BASE = basePath;
            process.stderr.write(chalk.green(`✓ Resumed worktree ${chalk.bold(wt.name)}\n`));
            process.stderr.write(chalk.dim(`  path   ${wt.path}\n`));
            process.stderr.write(chalk.dim(`  branch ${wt.branch}\n\n`));
            return;
        }
        if (withChanges.length > 1) {
            // Multiple active worktrees — show them and ask user to pick
            process.stderr.write(chalk.yellow(`${withChanges.length} worktrees have unmerged changes:\n\n`));
            for (const wt of withChanges) {
                const status = getWorktreeStatus(ext, basePath, wt.name, wt.path);
                process.stderr.write(formatStatus(status) + '\n\n');
            }
            process.stderr.write(chalk.dim('Specify which one: nerf -w <name>\n'));
            process.exit(0);
        }
        // No active worktrees — create a new one
        const name = generateWorktreeName();
        await createAndEnter(ext, basePath, name);
        return;
    }
    // nerf -w <name> — create or resume named worktree
    const name = worktreeFlag;
    const existing = ext.listWorktrees(basePath);
    const found = existing.find(wt => wt.name === name);
    if (found) {
        process.chdir(found.path);
        process.env.NERF_CLI_WORKTREE = name;
        process.env.NERF_CLI_WORKTREE_BASE = basePath;
        process.stderr.write(chalk.green(`✓ Resumed worktree ${chalk.bold(name)}\n`));
        process.stderr.write(chalk.dim(`  path   ${found.path}\n`));
        process.stderr.write(chalk.dim(`  branch ${found.branch}\n\n`));
    }
    else {
        await createAndEnter(ext, basePath, name);
    }
}
async function createAndEnter(ext, basePath, name) {
    try {
        const info = ext.createWorktree(basePath, name);
        const hookError = ext.runWorktreePostCreateHook(basePath, info.path);
        if (hookError) {
            process.stderr.write(chalk.yellow(`[nerf] ${hookError}\n`));
        }
        process.chdir(info.path);
        process.env.NERF_CLI_WORKTREE = name;
        process.env.NERF_CLI_WORKTREE_BASE = basePath;
        process.stderr.write(chalk.green(`✓ Created worktree ${chalk.bold(name)}\n`));
        process.stderr.write(chalk.dim(`  path   ${info.path}\n`));
        process.stderr.write(chalk.dim(`  branch ${info.branch}\n\n`));
    }
    catch (err) {
        const msg = err instanceof Error ? err.message : String(err);
        process.stderr.write(chalk.red(`[nerf] Failed to create worktree: ${msg}\n`));
        process.exit(1);
    }
}
// ─── Exports ────────────────────────────────────────────────────────────────
export { handleList, handleMerge, handleClean, handleRemove, handleStatusBanner, handleWorktreeFlag, getWorktreeStatus, };
