import { Args, Flags } from '@oclif/core';
import inquirer from 'inquirer';
import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../../lib/pmo/index.js';
import { styles } from '../../../lib/styles.js';
import { shouldOutputJson, outputPromptAsJson, outputErrorAsJson, createMetadata, buildPromptConfig, } from '../../../lib/prompt-json.js';
export default class EpicLink extends PMOCommand {
    static description = 'Manage epic dependencies (links)';
    static examples = [
        '<%= config.bin %> <%= command.id %> EPIC-001                     # List dependencies',
        '<%= config.bin %> <%= command.id %> EPIC-001 --blocks EPIC-002   # EPIC-001 is blocked by EPIC-002',
        '<%= config.bin %> <%= command.id %> EPIC-001 --relates EPIC-002  # EPIC-001 relates to EPIC-002',
        '<%= config.bin %> <%= command.id %> EPIC-001 --duplicates EPIC-002',
        '<%= config.bin %> <%= command.id %> EPIC-001 --all               # Show all links',
    ];
    static args = {
        id: Args.string({
            description: 'Epic ID',
            required: false,
        }),
    };
    static flags = {
        ...pmoBaseFlags,
        project: Flags.string({
            char: 'P',
            description: 'Project ID (default: "default")',
        }),
        blocks: Flags.string({
            char: 'b',
            description: 'Add blocking dependency: this epic is blocked by TARGET',
        }),
        relates: Flags.string({
            char: 'r',
            description: 'Add relates_to dependency',
        }),
        duplicates: Flags.string({
            char: 'd',
            description: 'Add duplicates dependency',
        }),
        all: Flags.boolean({
            char: 'a',
            description: 'Show all dependencies (blockers and blocking)',
            default: false,
        }),
        json: Flags.boolean({
            char: 'm',
            aliases: ['machine'],
            description: 'Output prompt configuration as JSON (for AI agents/scripts)',
            default: false,
        }),
    };
    async execute() {
        const { args, flags } = await this.parse(EpicLink);
        // Check if JSON output mode is active
        const jsonMode = shouldOutputJson(flags);
        // Helper to handle errors in JSON mode
        const handleError = (code, message) => {
            if (jsonMode) {
                outputErrorAsJson(code, message, createMetadata('epic link', flags));
                this.exit(1);
            }
            this.error(message);
        };
        const projectId = await this.requireProject();
        let epicId = args.id;
        if (!epicId) {
            const epics = await this.storage.listEpics(projectId);
            if (epics.length === 0) {
                if (jsonMode) {
                    outputErrorAsJson('NO_EPICS', 'No epics found.', createMetadata('epic link', flags));
                    return;
                }
                this.log(styles.muted('\nNo epics found.'));
                return;
            }
            // In JSON mode, output epic selection prompt
            if (jsonMode) {
                const epicChoices = epics.map(e => ({ name: `${e.id} - ${e.title}`, value: e.id }));
                outputPromptAsJson(buildPromptConfig('list', 'id', 'Select epic to manage dependencies:', epicChoices), createMetadata('epic link', flags));
                return;
            }
            const { selected } = await inquirer.prompt([{
                    type: 'list',
                    name: 'selected',
                    message: 'Select epic to manage dependencies:',
                    choices: epics.map(e => ({ name: `${e.id} - ${e.title}`, value: e.id })),
                }]);
            epicId = selected;
        }
        const epic = await this.storage.getEpic(epicId);
        if (!epic) {
            return handleError('EPIC_NOT_FOUND', `Epic not found: ${epicId}`);
        }
        // If a dependency flag is provided, add the dependency directly
        if (flags.blocks || flags.relates || flags.duplicates) {
            const targetId = flags.blocks || flags.relates || flags.duplicates;
            const dependencyType = flags.blocks ? 'blocks' :
                flags.relates ? 'relates_to' : 'duplicates';
            await this.addDependency(epicId, targetId, dependencyType, epic.title);
            return;
        }
        // Interactive mode: show menu in a loop
        // In JSON mode, output the interactive menu config instead of prompting
        if (jsonMode) {
            const menuChoices = [
                { name: 'View dependencies', value: 'view' },
                { name: 'Add blocking dependency (blocked by...)', value: 'blocks' },
                { name: 'Add relates_to dependency', value: 'relates_to' },
                { name: 'Add duplicates dependency', value: 'duplicates' },
                { name: 'Remove dependency', value: 'remove' },
                { name: 'Done', value: 'done' },
            ];
            outputPromptAsJson(buildPromptConfig('list', 'action', `Dependencies for ${epicId}:`, menuChoices), createMetadata('epic link', flags));
            return;
        }
        // Check for TTY before showing interactive menu
        if (!process.stdin.isTTY) {
            this.error('Interactive mode requires a TTY. Use --json for scripted usage or provide flags like --blocks, --relates, or --duplicates.');
        }
        let continueLoop = true;
        while (continueLoop) {
            // eslint-disable-next-line no-await-in-loop -- Interactive user loop
            const allEpics = await this.storage.listEpics(projectId);
            const otherEpics = allEpics.filter(e => e.id !== epicId);
            // eslint-disable-next-line no-await-in-loop -- Interactive user prompt
            const { action } = await inquirer.prompt([{
                    type: 'list',
                    name: 'action',
                    message: `Dependencies for ${epic.id}:`,
                    choices: [
                        { name: 'View dependencies', value: 'view' },
                        { name: 'Add blocking dependency (blocked by...)', value: 'blocks' },
                        { name: 'Add relates_to dependency', value: 'relates_to' },
                        { name: 'Add duplicates dependency', value: 'duplicates' },
                        new inquirer.Separator(),
                        { name: 'Remove dependency', value: 'remove' },
                        { name: 'Done', value: 'done' },
                    ],
                }]);
            if (action === 'done') {
                continueLoop = false;
                continue;
            }
            if (action === 'view') {
                // eslint-disable-next-line no-await-in-loop -- User action handling
                await this.viewDependencies(epicId, epic, flags.all);
                continue;
            }
            if (action === 'remove') {
                // eslint-disable-next-line no-await-in-loop -- User action handling
                const dependencies = await this.storage.listEpicDependencies(epicId);
                if (dependencies.length === 0) {
                    this.log(styles.muted('\nNo dependencies to remove.'));
                    continue;
                }
                // eslint-disable-next-line no-await-in-loop -- Building choices for current interaction
                const choices = await Promise.all(dependencies.map(async (dep) => {
                    const depEpic = await this.storage.getEpic(dep.dependsOnEpicId);
                    return {
                        name: `${dep.dependsOnEpicId} - ${depEpic?.title || 'Unknown'} (${dep.dependencyType})`,
                        value: { targetId: dep.dependsOnEpicId, type: dep.dependencyType }
                    };
                }));
                // eslint-disable-next-line no-await-in-loop -- User selection prompt
                const { selected } = await inquirer.prompt([{
                        type: 'list',
                        name: 'selected',
                        message: 'Select dependency to remove:',
                        choices,
                    }]);
                // eslint-disable-next-line no-await-in-loop -- Action after user selection
                await this.storage.deleteEpicDependency(epicId, selected.targetId, selected.type);
                // eslint-disable-next-line no-await-in-loop
                await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
                this.log(styles.success(`\n✅ Removed dependency: ${epicId} → ${selected.targetId}`));
                continue;
            }
            // Add dependency
            if (otherEpics.length === 0) {
                this.log(styles.muted('\nNo other epics to link to.'));
                continue;
            }
            // eslint-disable-next-line no-await-in-loop -- User selection prompt
            const { targetId } = await inquirer.prompt([{
                    type: 'list',
                    name: 'targetId',
                    message: `Select epic that ${epicId} ${action === 'blocks' ? 'is blocked by' : action === 'relates_to' ? 'relates to' : 'duplicates'}:`,
                    choices: otherEpics.map(e => ({ name: `${e.id} - ${e.title}`, value: e.id })),
                }]);
            // eslint-disable-next-line no-await-in-loop -- Action after user selection
            await this.addDependency(epicId, targetId, action, epic.title);
        }
    }
    async addDependency(epicId, targetId, dependencyType, epicTitle) {
        const targetEpic = await this.storage.getEpic(targetId);
        if (!targetEpic) {
            this.error(`Epic not found: ${targetId}`);
        }
        try {
            await this.storage.createEpicDependency(epicId, targetId, dependencyType);
            await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
            const typeLabel = dependencyType === 'blocks' ? 'is blocked by' :
                dependencyType === 'relates_to' ? 'relates to' : 'duplicates';
            this.log(styles.success(`\n✅ ${styles.emphasis(epicId)} ${typeLabel} ${styles.emphasis(targetId)}`));
            this.log(styles.muted(`   ${epicTitle}`));
            this.log(styles.muted(`   ${typeLabel} ${targetEpic.title}`));
        }
        catch (error) {
            if (error instanceof Error) {
                if (error.message.includes('already exists')) {
                    this.error('Dependency already exists');
                }
                if (error.message.includes('self-dependency')) {
                    this.error('Cannot create self-dependency');
                }
            }
            throw error;
        }
    }
    async viewDependencies(epicId, epic, showAll) {
        const dependencies = await this.storage.listEpicDependencies(epicId);
        const isBlocked = await this.storage.isEpicBlocked(epicId);
        this.log(`\n${styles.emphasis(epic.id)}: ${epic.title}`);
        if (isBlocked) {
            this.log(styles.warning('  Status: BLOCKED'));
        }
        const blockers = dependencies.filter(d => d.dependencyType === 'blocks');
        if (blockers.length > 0) {
            this.log(styles.muted('\n  Blocked by:'));
            // Fetch all blocker epics in parallel
            const blockerEpics = await Promise.all(blockers.map(dep => this.storage.getEpic(dep.dependsOnEpicId)));
            for (const blockerEpic of blockerEpics) {
                if (blockerEpic) {
                    const status = blockerEpic.status === 'complete' ? styles.success('complete') : styles.warning(blockerEpic.status);
                    this.log(`    - ${blockerEpic.id}: ${blockerEpic.title} (${status})`);
                }
            }
        }
        const otherDeps = dependencies.filter(d => d.dependencyType !== 'blocks');
        if (otherDeps.length > 0) {
            this.log(styles.muted('\n  Related:'));
            // Fetch all related epics in parallel
            const relatedEpics = await Promise.all(otherDeps.map(async (dep) => ({ dep, epic: await this.storage.getEpic(dep.dependsOnEpicId) })));
            for (const { dep, epic: relatedEpic } of relatedEpics) {
                if (relatedEpic) {
                    this.log(`    - ${dep.dependencyType}: ${relatedEpic.id} - ${relatedEpic.title}`);
                }
            }
        }
        if (showAll) {
            const allEpics = await this.storage.listEpics(epic.projectId);
            // Find all epics that depend on this epic in parallel
            const blockingResults = await Promise.all(allEpics
                .filter(otherEpic => otherEpic.id !== epicId)
                .map(async (otherEpic) => {
                const otherDeps = await this.storage.listEpicDependencies(otherEpic.id);
                const blockingDep = otherDeps.find(d => d.dependsOnEpicId === epicId);
                return blockingDep ? { epic: otherEpic, type: blockingDep.dependencyType } : null;
            }));
            const blocking = blockingResults.filter((b) => b !== null);
            if (blocking.length > 0) {
                this.log(styles.muted('\n  Blocking:'));
                for (const item of blocking) {
                    this.log(`    - ${item.epic.id}: ${item.epic.title} (${item.type})`);
                }
            }
        }
        if (dependencies.length === 0) {
            this.log(styles.muted('\n  No dependencies.'));
        }
        this.log('');
    }
}
