import { existsSync } from 'node:fs';
import { parseArgs } from 'node:util';
import { z } from 'zod';
import { SeverityThresholdSchema } from '../types/index.js';
import { getVersion } from '../utils/index.js';
export const CLIOptionsSchema = z.object({
    targets: z.array(z.string()).optional(),
    skill: z.string().optional(),
    config: z.string().optional(),
    json: z.boolean().default(false),
    /** Write full run output to a JSONL file */
    output: z.string().optional(),
    failOn: SeverityThresholdSchema.optional(),
    /** Only show findings at or above this severity in output */
    commentOn: SeverityThresholdSchema.optional(),
    help: z.boolean().default(false),
    /** Max concurrent trigger/skill executions (default: 4) */
    parallel: z.number().int().positive().optional(),
    /** Model to use for analysis (fallback when not set in config) */
    model: z.string().optional(),
    // Verbosity options
    quiet: z.boolean().default(false),
    verbose: z.number().default(0),
    color: z.boolean().optional(),
    /** Automatically apply all suggested fixes */
    fix: z.boolean().default(false),
    /** Overwrite existing files (for init command) */
    force: z.boolean().default(false),
    /** List available skills (for add command) */
    list: z.boolean().default(false),
    /** Force interpretation of ambiguous targets as git refs */
    git: z.boolean().default(false),
    /** Remote repository reference for skills (e.g., "owner/repo" or "owner/repo@sha") */
    remote: z.string().optional(),
    /** Skip network operations - only use cached remote skills */
    offline: z.boolean().default(false),
});
export function showVersion() {
    console.log(`warden ${getVersion()}`);
}
const HELP_TEXT = `
Usage: warden [command] [targets...] [options]

Analyze code for security issues and code quality.

Commands:
  init                 Initialize warden.toml and GitHub workflow
  add [skill]          Add a skill trigger to warden.toml
  sync [remote]        Update cached remote skills to latest
  setup-app            Create a GitHub App for Warden via manifest flow
  (default)            Run analysis on targets or using warden.toml triggers

Targets:
  <files>              Analyze specific files (e.g., src/auth.ts)
  <glob>               Analyze files matching pattern (e.g., "src/**/*.ts")
  <git-ref>            Analyze changes from git ref (e.g., HEAD~3, main..feature)
  (none)               Analyze uncommitted changes using warden.toml triggers

Options:
  --skill <name>       Run only this skill (default: run all built-in skills)
  --config <path>      Path to warden.toml (default: ./warden.toml)
  -m, --model <model>  Model to use (fallback when not set in config)
  --json               Output results as JSON
  -o, --output <path>  Write full run output to a JSONL file
  --fail-on <severity> Exit with code 1 if findings >= severity
                       (off, critical, high, medium, low, info)
  --comment-on <sev>   Only show findings >= severity in output
                       (off, critical, high, medium, low, info)
  --fix                Automatically apply all suggested fixes
  --parallel <n>       Max concurrent trigger/skill executions (default: 4)
  --git                Force ambiguous targets to be treated as git refs
  --quiet              Errors and final summary only
  -v, --verbose        Show real-time findings and hunk details
  -vv                  Show debug info (token counts, latencies)
  --color / --no-color Override color detection
  --help, -h           Show this help message
  --version, -V        Show version number

Init Options:
  -f, --force          Overwrite existing files

Add Options:
  --list               List available skills
  --remote <ref>       Remote repository (owner/repo, URL, or with @sha)

Run Options:
  --offline            Use cached remote skills without network access

Setup-app Options:
  --org <name>         Create under organization (default: personal)
  --port <number>      Local server port (default: 3000)
  --timeout <sec>      Callback timeout in seconds (default: 300)
  --name <string>      Custom app name (default: Warden)
  --no-open            Print URL instead of opening browser

Examples:
  warden init                             # Initialize warden configuration
  warden add                              # Interactive skill selection
  warden add security-review              # Add specific skill trigger
  warden add --list                       # List available skills
  warden add --remote getsentry/skills --skill security-review
                                          # Add remote skill trigger
  warden add --remote https://github.com/getsentry/skills --skill security-review
                                          # Add remote skill via URL
  warden add --remote getsentry/skills@abc123 --skill security-review
                                          # Add pinned remote skill
  warden                                  # Run triggers from warden.toml
  warden src/auth.ts                      # Run all skills on file
  warden src/auth.ts --skill security-review
                                          # Run specific skill on file
  warden "src/**/*.ts"                    # Run all skills on glob pattern
  warden HEAD~3                           # Run all skills on git changes
  warden HEAD~3 --skill security-review   # Run specific skill on git changes
  warden --json                           # Output as JSON
  warden --fail-on high                   # Fail if high+ severity findings
  warden --offline                        # Use cached skills only
  warden sync                             # Update all unpinned remote skills
  warden setup-app                        # Create GitHub App interactively
  warden setup-app --org myorg            # Create app under organization
`;
export function showHelp() {
    console.log(HELP_TEXT.trim());
}
/**
 * Detect if a target looks like a git ref vs a file path.
 * Returns 'git' for git refs, 'file' for file paths.
 *
 * For ambiguous targets (no path separators, no extension), checks
 * if a file/directory exists at that path before defaulting to git ref.
 */
export function detectTargetType(target, options = {}) {
    const { cwd = process.cwd(), forceGit = false } = options;
    // Git range syntax (e.g., main..feature, HEAD~3..HEAD)
    if (target.includes('..')) {
        return 'git';
    }
    // Relative ref syntax (e.g., HEAD~3, main^2)
    if (/[~^]\d*$/.test(target)) {
        return 'git';
    }
    // Common git refs
    if (/^(HEAD|FETCH_HEAD|ORIG_HEAD|MERGE_HEAD)$/i.test(target)) {
        return 'git';
    }
    // Contains path separators or glob characters → file
    if (target.includes('/') || target.includes('*') || target.includes('?')) {
        return 'file';
    }
    // Has a file extension → file
    if (/\.\w+$/.test(target)) {
        return 'file';
    }
    // Ambiguous target (no path separators, no extension)
    // If --git flag is set, force git ref interpretation
    if (forceGit) {
        return 'git';
    }
    // Check if file/directory exists at this path
    const fullPath = `${cwd}/${target}`;
    if (existsSync(fullPath)) {
        return 'file';
    }
    // Default to git ref (will be validated later)
    return 'git';
}
/**
 * Classify targets into git refs and file patterns.
 */
export function classifyTargets(targets, options = {}) {
    const gitRefs = [];
    const filePatterns = [];
    for (const target of targets) {
        if (detectTargetType(target, options) === 'git') {
            gitRefs.push(target);
        }
        else {
            filePatterns.push(target);
        }
    }
    return { gitRefs, filePatterns };
}
/**
 * Resolve color option from --color / --no-color flags.
 * Returns undefined for auto-detect, true for forced color, false for no color.
 */
function resolveColorOption(values) {
    if (values['no-color']) {
        return false;
    }
    if (values.color) {
        return true;
    }
    return undefined;
}
export function parseCliArgs(argv = process.argv.slice(2)) {
    // Count -v flags before parsing (parseArgs doesn't handle multiple -v well)
    let verboseCount = 0;
    const filteredArgv = argv.filter((arg) => {
        if (arg === '-v' || arg === '--verbose') {
            verboseCount++;
            return false;
        }
        if (arg === '-vv') {
            verboseCount += 2;
            return false;
        }
        return true;
    });
    const { values, positionals } = parseArgs({
        args: filteredArgv,
        options: {
            skill: { type: 'string' },
            config: { type: 'string' },
            model: { type: 'string', short: 'm' },
            json: { type: 'boolean', default: false },
            output: { type: 'string', short: 'o' },
            'fail-on': { type: 'string' },
            'comment-on': { type: 'string' },
            fix: { type: 'boolean', default: false },
            force: { type: 'boolean', short: 'f', default: false },
            list: { type: 'boolean', short: 'l', default: false },
            remote: { type: 'string' },
            offline: { type: 'boolean', default: false },
            parallel: { type: 'string' },
            git: { type: 'boolean', default: false },
            help: { type: 'boolean', short: 'h', default: false },
            version: { type: 'boolean', short: 'V', default: false },
            quiet: { type: 'boolean', default: false },
            color: { type: 'boolean' },
            'no-color': { type: 'boolean' },
            // setup-app options
            org: { type: 'string' },
            port: { type: 'string' },
            timeout: { type: 'string' },
            name: { type: 'string' },
            open: { type: 'boolean', default: true },
            'no-open': { type: 'boolean' },
        },
        allowPositionals: true,
    });
    if (values.version) {
        return {
            command: 'version',
            options: CLIOptionsSchema.parse({}),
        };
    }
    if (values.help) {
        return {
            command: 'help',
            options: CLIOptionsSchema.parse({ help: true }),
        };
    }
    // Filter out known commands from positionals
    const commands = ['run', 'help', 'init', 'add', 'version', 'setup-app', 'sync'];
    const targets = positionals.filter((p) => !commands.includes(p));
    // Handle explicit help command
    if (positionals.includes('help')) {
        return {
            command: 'help',
            options: CLIOptionsSchema.parse({ help: true }),
        };
    }
    // Handle explicit version command
    if (positionals.includes('version')) {
        return {
            command: 'version',
            options: CLIOptionsSchema.parse({}),
        };
    }
    // Handle init command
    if (positionals.includes('init')) {
        return {
            command: 'init',
            options: CLIOptionsSchema.parse({
                force: values.force,
                quiet: values.quiet,
                color: resolveColorOption(values),
            }),
        };
    }
    // Handle add command
    if (positionals.includes('add')) {
        // First positional after 'add' is the skill name
        const addIndex = positionals.indexOf('add');
        const skillArg = positionals[addIndex + 1];
        return {
            command: 'add',
            options: CLIOptionsSchema.parse({
                skill: values.skill ?? skillArg,
                list: values.list,
                remote: values.remote,
                quiet: values.quiet,
                color: resolveColorOption(values),
            }),
        };
    }
    // Handle sync command
    if (positionals.includes('sync')) {
        // First positional after 'sync' is the remote to sync, --remote flag takes precedence
        const syncIndex = positionals.indexOf('sync');
        const remoteArg = values.remote ?? positionals[syncIndex + 1];
        return {
            command: 'sync',
            options: CLIOptionsSchema.parse({
                remote: remoteArg,
                quiet: values.quiet,
                color: resolveColorOption(values),
            }),
        };
    }
    // Handle setup-app command
    if (positionals.includes('setup-app')) {
        return {
            command: 'setup-app',
            options: CLIOptionsSchema.parse({
                quiet: values.quiet,
                color: resolveColorOption(values),
            }),
            setupAppOptions: {
                org: values.org,
                port: values.port ? parseInt(values.port, 10) : 3000,
                timeout: values.timeout ? parseInt(values.timeout, 10) : 300,
                name: values.name,
                open: !values['no-open'],
            },
        };
    }
    const rawOptions = {
        targets: targets.length > 0 ? targets : undefined,
        skill: values.skill,
        config: values.config,
        model: values.model,
        json: values.json,
        output: values.output,
        failOn: values['fail-on'],
        commentOn: values['comment-on'],
        fix: values.fix,
        force: values.force,
        parallel: values.parallel ? parseInt(values.parallel, 10) : undefined,
        git: values.git,
        offline: values.offline,
        help: values.help,
        quiet: values.quiet,
        verbose: verboseCount,
        color: resolveColorOption(values),
    };
    const result = CLIOptionsSchema.safeParse(rawOptions);
    if (!result.success) {
        const issues = result.error.issues.map((i) => `  - ${i.path.join('.')}: ${i.message}`);
        console.error('Invalid options:');
        console.error(issues.join('\n'));
        process.exit(1);
    }
    return {
        command: 'run',
        options: result.data,
    };
}
//# sourceMappingURL=args.js.map