/**
 * cv hooks command
 * Manage git hooks for automatic knowledge graph sync
 */
import { Command } from 'commander';
import chalk from 'chalk';
import { promises as fs } from 'fs';
import * as path from 'path';
import { findRepoRoot } from '@cv-git/shared';
import { addGlobalOptions } from '../utils/output.js';
const HOOK_MARKER = '# CV-GIT HOOK';
const POST_COMMIT_HOOK = `#!/bin/sh
${HOOK_MARKER} - DO NOT EDIT THIS LINE
# Auto-sync knowledge graph after commit (with delta summaries)
# Runs in background to avoid slowing down commits

cv sync --incremental --summaries --quiet 2>/dev/null &
`;
const POST_MERGE_HOOK = `#!/bin/sh
${HOOK_MARKER} - DO NOT EDIT THIS LINE
# Auto-sync knowledge graph after merge/pull
# Runs in background to avoid slowing down merges

cv sync --incremental --summaries --quiet 2>/dev/null &
`;
const POST_CHECKOUT_HOOK = `#!/bin/sh
${HOOK_MARKER} - DO NOT EDIT THIS LINE
# Auto-sync knowledge graph after branch checkout
# Runs in background to avoid slowing down checkouts

cv sync --incremental --summaries --quiet 2>/dev/null &
`;
const PREPARE_COMMIT_MSG_HOOK = `#!/bin/sh
${HOOK_MARKER} - DO NOT EDIT THIS LINE
# Generate AI commit message using CV-Git knowledge graph analysis
#
# Arguments:
#   $1 - Path to the file containing the commit message
#   $2 - Source of the commit message (message, template, merge, squash, commit)
#   $3 - Commit SHA (only for squash or commit)
#
# Only generates if:
#   - Source is empty (no -m flag) or "template"
#   - There are staged changes
#   - CV-Git is initialized

COMMIT_MSG_FILE="$1"
COMMIT_SOURCE="$2"

# Skip if message was provided via -m flag or other sources
if [ "$COMMIT_SOURCE" = "message" ] || [ "$COMMIT_SOURCE" = "merge" ] || [ "$COMMIT_SOURCE" = "squash" ] || [ "$COMMIT_SOURCE" = "commit" ]; then
  exit 0
fi

# Check if there are staged changes
if ! git diff --cached --quiet 2>/dev/null; then
  # Generate commit message
  GENERATED_MSG=$(cv commit --generate --dry-run --quiet 2>/dev/null)

  if [ -n "$GENERATED_MSG" ] && [ $? -eq 0 ]; then
    # Prepend generated message to the commit message file
    # Keep existing content (template, comments) below
    EXISTING_CONTENT=$(cat "$COMMIT_MSG_FILE")

    echo "$GENERATED_MSG" > "$COMMIT_MSG_FILE"
    echo "" >> "$COMMIT_MSG_FILE"
    echo "# --- Generated by CV-Git (edit as needed) ---" >> "$COMMIT_MSG_FILE"
    echo "" >> "$COMMIT_MSG_FILE"
    echo "$EXISTING_CONTENT" >> "$COMMIT_MSG_FILE"
  fi
fi

exit 0
`;
export function hooksCommand() {
    const cmd = new Command('hooks');
    cmd.description('Manage git hooks for automatic sync');
    // cv hooks install
    cmd
        .command('install')
        .description('Install git hooks for automatic sync and AI commit messages')
        .option('--post-commit', 'Only install post-commit hook (auto-sync)')
        .option('--post-merge', 'Only install post-merge hook (auto-sync)')
        .option('--post-checkout', 'Only install post-checkout hook (auto-sync)')
        .option('--prepare-commit-msg', 'Only install prepare-commit-msg hook (AI commit messages)')
        .option('--ai-commit', 'Alias for --prepare-commit-msg')
        .action(async (options) => {
        try {
            const repoRoot = await findRepoRoot();
            if (!repoRoot) {
                console.error(chalk.red('Not in a git repository'));
                process.exit(1);
            }
            const hooksDir = path.join(repoRoot, '.git', 'hooks');
            // Ensure hooks directory exists
            await fs.mkdir(hooksDir, { recursive: true });
            const wantsPrepareCommitMsg = options.prepareCommitMsg || options.aiCommit;
            const installAll = !options.postCommit && !options.postMerge && !options.postCheckout && !wantsPrepareCommitMsg;
            let installed = 0;
            // Install post-commit hook
            if (installAll || options.postCommit) {
                const hookPath = path.join(hooksDir, 'post-commit');
                const result = await installHook(hookPath, POST_COMMIT_HOOK, 'post-commit');
                if (result)
                    installed++;
            }
            // Install post-merge hook
            if (installAll || options.postMerge) {
                const hookPath = path.join(hooksDir, 'post-merge');
                const result = await installHook(hookPath, POST_MERGE_HOOK, 'post-merge');
                if (result)
                    installed++;
            }
            // Install post-checkout hook
            if (installAll || options.postCheckout) {
                const hookPath = path.join(hooksDir, 'post-checkout');
                const result = await installHook(hookPath, POST_CHECKOUT_HOOK, 'post-checkout');
                if (result)
                    installed++;
            }
            // Install prepare-commit-msg hook
            if (installAll || wantsPrepareCommitMsg) {
                const hookPath = path.join(hooksDir, 'prepare-commit-msg');
                const result = await installHook(hookPath, PREPARE_COMMIT_MSG_HOOK, 'prepare-commit-msg');
                if (result)
                    installed++;
            }
            if (installed > 0) {
                console.log(chalk.green(`\n✓ Installed ${installed} hook(s)`));
                console.log(chalk.gray('\nHook behavior:'));
                if (installAll || options.postCommit) {
                    console.log(chalk.gray('  • post-commit: auto-sync knowledge graph'));
                }
                if (installAll || options.postMerge) {
                    console.log(chalk.gray('  • post-merge: auto-sync knowledge graph'));
                }
                if (installAll || options.postCheckout) {
                    console.log(chalk.gray('  • post-checkout: auto-sync knowledge graph'));
                }
                if (installAll || wantsPrepareCommitMsg) {
                    console.log(chalk.cyan('  • prepare-commit-msg: AI commit message generation'));
                    console.log(chalk.gray('    When you run `git commit`, a message will be auto-generated'));
                    console.log(chalk.gray('    Edit in your editor before confirming'));
                }
            }
            else {
                console.log(chalk.yellow('\nNo hooks installed (all already present)'));
            }
        }
        catch (error) {
            console.error(chalk.red(`Error: ${error.message}`));
            process.exit(1);
        }
    });
    // cv hooks uninstall
    cmd
        .command('uninstall')
        .description('Remove cv-git hooks')
        .option('--prepare-commit-msg', 'Only remove prepare-commit-msg hook')
        .option('--ai-commit', 'Alias for --prepare-commit-msg')
        .action(async (options) => {
        try {
            const repoRoot = await findRepoRoot();
            if (!repoRoot) {
                console.error(chalk.red('Not in a git repository'));
                process.exit(1);
            }
            const hooksDir = path.join(repoRoot, '.git', 'hooks');
            let removed = 0;
            const wantsPrepareCommitMsgOnly = options.prepareCommitMsg || options.aiCommit;
            const hooks = wantsPrepareCommitMsgOnly
                ? ['prepare-commit-msg']
                : ['post-commit', 'post-merge', 'post-checkout', 'prepare-commit-msg'];
            for (const hookName of hooks) {
                const hookPath = path.join(hooksDir, hookName);
                const result = await uninstallHook(hookPath, hookName);
                if (result)
                    removed++;
            }
            if (removed > 0) {
                console.log(chalk.green(`\n✓ Removed ${removed} hook(s)`));
            }
            else {
                console.log(chalk.yellow('\nNo cv-git hooks found'));
            }
        }
        catch (error) {
            console.error(chalk.red(`Error: ${error.message}`));
            process.exit(1);
        }
    });
    // cv hooks list
    cmd
        .command('list')
        .description('List all installed git hooks')
        .option('--all', 'Show all hooks (not just cv-git hooks)')
        .action(async (options) => {
        try {
            const repoRoot = await findRepoRoot();
            if (!repoRoot) {
                console.error(chalk.red('Not in a git repository'));
                process.exit(1);
            }
            const hooksDir = path.join(repoRoot, '.git', 'hooks');
            // Check if hooks directory exists
            try {
                await fs.access(hooksDir);
            }
            catch {
                console.log(chalk.yellow('\nNo hooks directory found.'));
                console.log(chalk.gray('Run: ') + chalk.cyan('cv hooks install'));
                return;
            }
            // Get all files in hooks directory
            const files = await fs.readdir(hooksDir);
            // Filter to actual hook files (not .sample files)
            const hookFiles = files.filter((f) => !f.endsWith('.sample') && !f.startsWith('.'));
            if (hookFiles.length === 0) {
                console.log(chalk.yellow('\nNo hooks installed.'));
                console.log(chalk.gray('Run: ') + chalk.cyan('cv hooks install'));
                return;
            }
            console.log(chalk.bold('\nInstalled Git Hooks:\n'));
            const cvGitHooks = [];
            const otherHooks = [];
            for (const hookFile of hookFiles.sort()) {
                const hookPath = path.join(hooksDir, hookFile);
                try {
                    const stat = await fs.stat(hookPath);
                    if (!stat.isFile())
                        continue;
                    const content = await fs.readFile(hookPath, 'utf-8');
                    const isCvGit = content.includes(HOOK_MARKER);
                    if (isCvGit) {
                        cvGitHooks.push(hookFile);
                    }
                    else {
                        otherHooks.push(hookFile);
                    }
                }
                catch {
                    // Skip files that can't be read
                }
            }
            // Show CV-Git hooks
            if (cvGitHooks.length > 0) {
                console.log(chalk.cyan('  CV-Git Hooks:'));
                for (const hook of cvGitHooks) {
                    const description = getHookDescription(hook);
                    console.log(chalk.green(`    ✓ ${hook}`) + chalk.gray(` - ${description}`));
                }
                console.log();
            }
            // Show other hooks
            if (options.all && otherHooks.length > 0) {
                console.log(chalk.cyan('  Other Hooks:'));
                for (const hook of otherHooks) {
                    console.log(chalk.yellow(`    ○ ${hook}`));
                }
                console.log();
            }
            // Summary
            if (cvGitHooks.length > 0) {
                console.log(chalk.gray(`Total: ${cvGitHooks.length} cv-git hook(s)`) +
                    (otherHooks.length > 0
                        ? chalk.gray(`, ${otherHooks.length} other hook(s)`)
                        : ''));
            }
            else {
                console.log(chalk.yellow('No cv-git hooks installed.'));
                console.log(chalk.gray('Run: ') + chalk.cyan('cv hooks install'));
            }
        }
        catch (error) {
            console.error(chalk.red(`Error: ${error.message}`));
            process.exit(1);
        }
    });
    // cv hooks status
    cmd
        .command('status')
        .description('Show installed hooks status')
        .action(async () => {
        try {
            const repoRoot = await findRepoRoot();
            if (!repoRoot) {
                console.error(chalk.red('Not in a git repository'));
                process.exit(1);
            }
            const hooksDir = path.join(repoRoot, '.git', 'hooks');
            console.log(chalk.bold('\nGit Hooks Status:\n'));
            const hookDescriptions = {
                'post-commit': 'auto-sync after commit',
                'post-merge': 'auto-sync after merge/pull',
                'post-checkout': 'auto-sync after branch checkout',
                'prepare-commit-msg': 'AI commit message generation'
            };
            for (const hookName of ['post-commit', 'post-merge', 'post-checkout', 'prepare-commit-msg']) {
                const hookPath = path.join(hooksDir, hookName);
                const status = await getHookStatus(hookPath);
                const icon = status === 'cv-git' ? chalk.green('✓') :
                    status === 'other' ? chalk.yellow('○') :
                        chalk.gray('·');
                const label = status === 'cv-git' ? chalk.green('cv-git hook installed') :
                    status === 'other' ? chalk.yellow('other hook present') :
                        chalk.gray('not installed');
                const desc = hookDescriptions[hookName] || '';
                console.log(`  ${icon} ${hookName}: ${label}`);
                if (status === 'cv-git') {
                    console.log(chalk.gray(`      ${desc}`));
                }
            }
            console.log();
        }
        catch (error) {
            console.error(chalk.red(`Error: ${error.message}`));
            process.exit(1);
        }
    });
    addGlobalOptions(cmd);
    return cmd;
}
/**
 * Install a hook, preserving existing non-cv hooks
 */
async function installHook(hookPath, hookContent, hookName) {
    try {
        // Check if hook already exists
        const existingContent = await fs.readFile(hookPath, 'utf-8').catch(() => null);
        if (existingContent) {
            // Check if it's our hook
            if (existingContent.includes(HOOK_MARKER)) {
                console.log(chalk.gray(`  ${hookName}: already installed`));
                return false;
            }
            // There's an existing hook that's not ours
            // Prepend our hook and call the original
            const combinedHook = hookContent + `
# Original hook preserved below
${existingContent.replace(/^#!.*\n/, '')}
`;
            await fs.writeFile(hookPath, combinedHook, { mode: 0o755 });
            console.log(chalk.green(`  ${hookName}: installed (preserved existing hook)`));
            return true;
        }
        // No existing hook, install fresh
        await fs.writeFile(hookPath, hookContent, { mode: 0o755 });
        console.log(chalk.green(`  ${hookName}: installed`));
        return true;
    }
    catch (error) {
        console.error(chalk.red(`  ${hookName}: failed - ${error.message}`));
        return false;
    }
}
/**
 * Uninstall our hook, preserving other hooks
 */
async function uninstallHook(hookPath, hookName) {
    try {
        const existingContent = await fs.readFile(hookPath, 'utf-8').catch(() => null);
        if (!existingContent) {
            return false;
        }
        if (!existingContent.includes(HOOK_MARKER)) {
            // Not our hook
            return false;
        }
        // Check if there's preserved content after our hook
        const preservedMatch = existingContent.match(/# Original hook preserved below\n([\s\S]*)/);
        if (preservedMatch && preservedMatch[1].trim()) {
            // Restore the original hook
            const originalContent = '#!/bin/sh\n' + preservedMatch[1];
            await fs.writeFile(hookPath, originalContent, { mode: 0o755 });
            console.log(chalk.green(`  ${hookName}: removed (restored original hook)`));
        }
        else {
            // Just delete the hook
            await fs.unlink(hookPath);
            console.log(chalk.green(`  ${hookName}: removed`));
        }
        return true;
    }
    catch (error) {
        console.error(chalk.red(`  ${hookName}: failed - ${error.message}`));
        return false;
    }
}
/**
 * Get hook status
 */
async function getHookStatus(hookPath) {
    try {
        const content = await fs.readFile(hookPath, 'utf-8');
        if (content.includes(HOOK_MARKER)) {
            return 'cv-git';
        }
        return 'other';
    }
    catch {
        return 'none';
    }
}
/**
 * Get human-readable description for a hook
 */
function getHookDescription(hookName) {
    const descriptions = {
        'post-commit': 'auto-sync after commit',
        'post-merge': 'auto-sync after merge/pull',
        'prepare-commit-msg': 'AI commit message generation',
        'pre-commit': 'runs before commit',
        'pre-push': 'runs before push',
        'commit-msg': 'validates commit message',
        'post-checkout': 'runs after checkout',
        'post-rewrite': 'runs after rebase/amend',
    };
    return descriptions[hookName] || 'custom hook';
}
//# sourceMappingURL=hooks.js.map