import { Args, Flags } from '@oclif/core';
import { PMOCommand, pmoBaseFlags, autoExportToBoard } from '../../lib/pmo/index.js';
import { styles } from '../../lib/styles.js';
import { shouldOutputJson, outputErrorAsJson, createMetadata, } from '../../lib/prompt-json.js';
export default class TicketEpic extends PMOCommand {
    static description = 'Assign ticket(s) to an epic (parent-child relationship)';
    static examples = [
        '<%= config.bin %> <%= command.id %> TKT-001 EPIC-001',
        '<%= config.bin %> <%= command.id %> TKT-001 --unlink',
        '<%= config.bin %> <%= command.id %>',
        '<%= config.bin %> <%= command.id %> --bulk --to-epic EPIC-001',
    ];
    static args = {
        id: Args.string({
            description: 'Ticket ID',
            required: false,
        }),
        'epic-id': Args.string({
            description: 'Epic ID to link to',
            required: false,
        }),
    };
    static flags = {
        ...pmoBaseFlags,
        unlink: Flags.boolean({
            char: 'u',
            description: 'Remove epic link instead of adding',
            default: false,
        }),
        bulk: Flags.boolean({
            char: 'b',
            description: 'Enable bulk mode to link multiple tickets to an epic',
            default: false,
        }),
        'to-epic': Flags.string({
            description: 'Target epic ID (for bulk mode)',
        }),
        'from-epic': Flags.string({
            description: 'Filter tickets by current epic (bulk mode)',
        }),
        force: Flags.boolean({
            char: 'f',
            description: 'Skip confirmation prompt (bulk mode)',
            default: false,
        }),
    };
    async execute() {
        const { args, flags } = await this.parse(TicketEpic);
        const filterProjectId = flags.project;
        // Check if JSON output mode is active
        const jsonMode = shouldOutputJson(flags);
        // Bulk mode
        if (flags.bulk) {
            await this.executeBulk(flags);
            return;
        }
        // Get all tickets
        const allTickets = await this.storage.listTickets(filterProjectId);
        if (allTickets.length === 0) {
            if (jsonMode) {
                outputErrorAsJson('NO_TICKETS', 'No tickets found.', createMetadata('ticket epic', flags));
                return;
            }
            this.log(styles.muted('\nNo tickets found.'));
            return;
        }
        // Get project for epics - use first ticket's project or prompt
        const projectId = await this.requireProject();
        // Get all epics
        const epics = await this.storage.listEpics(projectId);
        // Helper to get ticket's epic ID via storage layer
        const getTicketEpicId = (ticketId) => {
            const ticket = allTickets.find((t) => t.id === ticketId);
            return ticket?.epicId ?? null;
        };
        let ticketId = args.id;
        let epicId = args['epic-id'];
        // If unlinking, we don't need an epic ID
        if (flags.unlink) {
            epicId = undefined;
        }
        // If no ticket ID provided, prompt for selection
        if (!ticketId) {
            const ticketChoices = allTickets.map((t) => {
                const currentEpicId = getTicketEpicId(t.id);
                const epicLabel = currentEpicId
                    ? epics.find(e => e.id === currentEpicId)?.title || currentEpicId
                    : 'No epic';
                return {
                    id: t.id,
                    name: `${t.id} - ${t.title} (${t.statusName || t.status}) [${epicLabel}]`,
                };
            });
            const selected = await this.selectFromList({
                message: 'Select ticket to link:',
                items: ticketChoices,
                getName: (t) => t.name,
                getValue: (t) => t.id,
                getCommand: (t) => `prlt ticket epic ${t.id} -P ${projectId} --json`,
                jsonMode: jsonMode ? { flags, commandName: 'ticket epic' } : null,
            });
            if (!selected) {
                return;
            }
            ticketId = selected;
        }
        // Validate ticket exists
        const ticket = allTickets.find((t) => t.id === ticketId);
        if (!ticket) {
            this.error(`Ticket not found: ${ticketId}`);
        }
        const currentEpicId = getTicketEpicId(ticketId);
        // If unlinking, just remove the link
        if (flags.unlink) {
            if (!currentEpicId) {
                this.log(styles.muted(`\nTicket ${ticketId} is not linked to any epic.`));
                return;
            }
            const currentEpic = epics.find(e => e.id === currentEpicId);
            // Update the ticket
            await this.storage.unlinkTicketFromEpic(ticketId);
            await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
            this.log(styles.success(`\n✅ Unlinked ${styles.emphasis(ticketId)} from ${currentEpic?.title || currentEpicId}`));
            this.log(styles.muted(`   Title: ${ticket.title}`));
            return;
        }
        // If no epic ID provided, prompt for selection
        if (!epicId) {
            const epicChoices = epics.map(e => ({
                id: e.id,
                name: `${e.id} ${e.title} (${e.status})${e.id === currentEpicId ? ' ← current' : ''}`,
            }));
            if (epicChoices.length === 0) {
                this.log(styles.muted('\nNo epics found. Create one with: prlt epic create'));
                return;
            }
            // Add "None" option if ticket has an epic
            if (currentEpicId) {
                epicChoices.push({ id: '__none__', name: 'None (remove epic link)' });
            }
            const selected = await this.selectFromList({
                message: 'Link to which epic?',
                items: epicChoices,
                getName: (e) => e.name,
                getValue: (e) => e.id,
                getCommand: (e) => e.id === '__none__' ? `prlt ticket epic ${ticketId} --unlink -P ${projectId} --json` : `prlt ticket epic ${ticketId} ${e.id} -P ${projectId} --json`,
                jsonMode: jsonMode ? { flags, commandName: 'ticket epic' } : null,
            });
            if (!selected) {
                return;
            }
            if (selected === '__none__') {
                // Unlink
                await this.storage.unlinkTicketFromEpic(ticketId);
                const currentEpic = epics.find(e => e.id === currentEpicId);
                await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
                this.log(styles.success(`\n✅ Unlinked ${styles.emphasis(ticketId)} from ${currentEpic?.title || currentEpicId}`));
                this.log(styles.muted(`   Title: ${ticket.title}`));
                return;
            }
            epicId = selected;
        }
        // Validate epic exists
        const epic = epics.find(e => e.id === epicId);
        if (!epic) {
            this.error(`Epic not found: ${epicId}`);
        }
        // Check if already linked to this epic
        if (currentEpicId === epicId) {
            this.log(styles.muted(`\nTicket ${ticketId} is already linked to ${epic.title}.`));
            return;
        }
        // Update the ticket
        await this.storage.linkTicketToEpic(ticketId, epicId);
        await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
        this.log(styles.success(`\n✅ Linked ${styles.emphasis(ticketId)} to ${styles.emphasis(epicId)}`));
        this.log(styles.muted(`   Title: ${ticket.title}`));
        this.log(styles.muted(`   Epic: ${epic.title}`));
    }
    async executeBulk(flags) {
        const jsonMode = shouldOutputJson(flags);
        const jsonModeConfig = jsonMode ? { flags: flags, commandName: 'ticket epic' } : null;
        this.log(styles.emphasis('🔗 Link Tickets to Epic\n'));
        // Get project first
        const projectId = await this.requireProject();
        // Get all tickets
        const allTickets = await this.storage.listTickets(projectId);
        if (allTickets.length === 0) {
            this.log(styles.warning('No tickets found.'));
            return;
        }
        // Get epics via storage layer
        const epics = await this.storage.listEpics(projectId);
        // Filter tickets if --from-epic specified
        let filteredTickets = allTickets;
        if (flags['from-epic']) {
            filteredTickets = allTickets.filter(t => t.epicId === flags['from-epic']);
        }
        if (filteredTickets.length === 0) {
            this.log(styles.warning('No tickets found matching filter.'));
            return;
        }
        // Get current epic for each ticket (already available on Ticket objects)
        const ticketEpics = new Map();
        for (const ticket of filteredTickets) {
            ticketEpics.set(ticket.id, ticket.epicId ?? null);
        }
        // Select tickets to link
        const { selectedTickets } = await this.prompt([{
                type: 'checkbox',
                name: 'selectedTickets',
                message: 'Select tickets to link:',
                choices: filteredTickets.map(t => {
                    const epicId = ticketEpics.get(t.id);
                    const epicTitle = epicId ? epics.find(e => e.id === epicId)?.title || epicId : '(none)';
                    return {
                        name: `${t.id} - ${t.title}  [Epic: ${epicTitle}]`,
                        value: t.id,
                    };
                }),
            }], jsonModeConfig);
        if (selectedTickets.length === 0) {
            this.log(styles.muted('No tickets selected.'));
            return;
        }
        // Select target epic
        let targetEpic = flags['to-epic'];
        if (targetEpic === undefined) {
            const { epic } = await this.prompt([{
                    type: 'list',
                    name: 'epic',
                    message: 'Link to which epic?',
                    choices: [
                        { name: 'None (remove epic link)', value: null, command: 'prlt ticket epic --bulk --unlink --json' },
                        ...epics.map(e => ({
                            name: `${e.title} (${e.status})`,
                            value: e.id,
                            command: `prlt ticket epic --bulk --to-epic ${e.id} --json`,
                        })),
                    ],
                }], jsonModeConfig);
            targetEpic = epic;
        }
        // Confirmation
        if (!flags.force) {
            const epicLabel = targetEpic
                ? epics.find(e => e.id === targetEpic)?.title || targetEpic
                : 'None (removing link)';
            this.log(styles.primary('\nWill link to epic:'));
            this.log(styles.primary(`  → ${epicLabel}\n`));
            this.log(styles.primary('Tickets:'));
            for (const ticketId of selectedTickets) {
                const ticket = filteredTickets.find(t => t.id === ticketId);
                this.log(styles.primary(`  • ${ticketId}: ${ticket?.title}`));
            }
            this.log('');
            const { confirm } = await this.prompt([{
                    type: 'list',
                    name: 'confirm',
                    message: 'Continue?',
                    choices: [
                        { name: 'No, cancel', value: false, command: '' },
                        { name: 'Yes, link tickets', value: true, command: `prlt ticket epic --bulk --to-epic ${targetEpic || 'none'} --force --json` }
                    ],
                    default: 0
                }], jsonModeConfig);
            if (!confirm) {
                this.log(styles.muted('Operation cancelled.'));
                return;
            }
        }
        this.log('');
        // Link each ticket
        let successCount = 0;
        let failCount = 0;
        for (const ticketId of selectedTickets) {
            try {
                if (targetEpic) {
                    await this.storage.linkTicketToEpic(ticketId, targetEpic);
                }
                else {
                    await this.storage.unlinkTicketFromEpic(ticketId);
                }
                const action = targetEpic ? `Linked to ${targetEpic}` : 'Removed epic link';
                this.log(styles.success(`${ticketId}: ${action}`));
                successCount++;
            }
            catch (error) {
                this.log(styles.error(`Failed to link ${ticketId}: ${error instanceof Error ? error.message : String(error)}`));
                failCount++;
            }
        }
        // Auto-export to kanban.md
        await autoExportToBoard(this.pmoPath, this.storage, (msg) => this.log(styles.muted(msg)));
        // Summary
        this.log('');
        if (successCount > 0) {
            const action = targetEpic ? 'Linked' : 'Unlinked';
            this.log(styles.success(`${action} ${successCount} ticket(s)`));
        }
        if (failCount > 0) {
            this.log(styles.error(`Failed to link ${failCount} ticket(s)`));
        }
    }
}
//# sourceMappingURL=epic.js.map