/**
 * GitHubProjectAdapter - Sync projects to GitHub labels
 *
 * Subscribes to project events and creates/updates/archives
 * project labels in GitHub repositories.
 *
 * Label format: project:{project-id}
 */
import { consoleLogger } from '../../../utils/logger.js';
import { execFileNoThrow } from '../../../utils/execFileNoThrow.js';
const DEFAULT_LABEL_COLOR = '0052CC'; // Blue
export class GitHubProjectAdapter {
    constructor(options) {
        this.logger = options.logger ?? consoleLogger;
        this.owner = options.owner || '';
        this.repo = options.repo || '';
        this.token = options.token;
    }
    /**
     * Subscribe to project events from the registry's event bus
     */
    subscribe(registry) {
        this.registry = registry;
        const eventBus = registry.getEventBus();
        eventBus.onProjectCreated(this.handleProjectCreated.bind(this));
        eventBus.onProjectUpdated(this.handleProjectUpdated.bind(this));
        eventBus.onProjectDeleted(this.handleProjectDeleted.bind(this));
        eventBus.onProjectSyncRequested(this.handleSyncRequested.bind(this));
        this.logger.debug('GitHubProjectAdapter subscribed to project events');
    }
    /**
     * Handle ProjectCreated event - create label in GitHub
     */
    async handleProjectCreated(event) {
        const { project } = event;
        try {
            const labelName = this.getLabelName(project.id);
            const labelColor = project.external?.github?.labelColor ?? DEFAULT_LABEL_COLOR;
            await this.createLabel(labelName, labelColor, `Project: ${project.name}`);
            // Update registry with sync status
            if (this.registry) {
                await this.registry.updateProject(project.id, {
                    external: {
                        ...project.external,
                        github: {
                            labelName,
                            labelColor,
                            lastSynced: new Date().toISOString(),
                        },
                    },
                }, { emitEvent: false });
            }
            this.logger.info(`Created GitHub label: ${labelName}`);
        }
        catch (error) {
            this.logger.error(`Failed to create GitHub label for project ${project.id}: ${error}`);
            // Update registry with error
            if (this.registry) {
                await this.registry.updateProject(project.id, {
                    external: {
                        ...project.external,
                        github: {
                            labelName: this.getLabelName(project.id),
                            syncError: String(error),
                        },
                    },
                }, { emitEvent: false });
            }
        }
    }
    /**
     * Handle ProjectUpdated event - update label in GitHub
     */
    async handleProjectUpdated(event) {
        const { projectId, changes } = event;
        // Only sync if name or relevant fields changed
        if (!changes.name && !changes.external?.github) {
            return;
        }
        try {
            const project = await this.registry?.getProject(projectId);
            if (!project)
                return;
            const labelName = this.getLabelName(projectId);
            // Update label description if name changed
            if (changes.name) {
                await this.updateLabel(labelName, { description: `Project: ${changes.name}` });
            }
            // Update sync timestamp
            if (this.registry) {
                await this.registry.updateProject(projectId, {
                    external: {
                        ...project.external,
                        github: {
                            ...project.external?.github,
                            labelName,
                            lastSynced: new Date().toISOString(),
                            syncError: undefined,
                        },
                    },
                }, { emitEvent: false });
            }
            this.logger.info(`Updated GitHub label: ${labelName}`);
        }
        catch (error) {
            this.logger.error(`Failed to update GitHub label for project ${projectId}: ${error}`);
        }
    }
    /**
     * Handle ProjectDeleted event - archive label in GitHub
     */
    async handleProjectDeleted(event) {
        const { projectId } = event;
        try {
            const oldLabelName = this.getLabelName(projectId);
            const archivedLabelName = `_archived_${oldLabelName}`;
            // Rename label to archived
            await this.updateLabel(oldLabelName, { name: archivedLabelName });
            this.logger.info(`Archived GitHub label: ${oldLabelName} → ${archivedLabelName}`);
        }
        catch (error) {
            this.logger.error(`Failed to archive GitHub label for project ${projectId}: ${error}`);
        }
    }
    /**
     * Handle ProjectSyncRequested event - force sync to GitHub
     */
    async handleSyncRequested(event) {
        if (!event.targets.includes('github')) {
            return;
        }
        try {
            if (event.projectId) {
                // Sync single project
                const project = await this.registry?.getProject(event.projectId);
                if (project) {
                    await this.syncProject(project);
                }
            }
            else {
                // Sync all projects
                const projects = await this.registry?.listProjects();
                if (projects) {
                    for (const project of projects) {
                        await this.syncProject(project);
                    }
                }
            }
        }
        catch (error) {
            this.logger.error(`Failed to handle sync request: ${error}`);
        }
    }
    /**
     * Sync a project to GitHub
     */
    async syncProject(project) {
        const labelName = this.getLabelName(project.id);
        // Check if label exists
        const exists = await this.labelExists(labelName);
        if (exists) {
            // Update existing label
            await this.updateLabel(labelName, {
                description: `Project: ${project.name}`,
            });
        }
        else {
            // Create new label
            const labelColor = project.external?.github?.labelColor ?? DEFAULT_LABEL_COLOR;
            await this.createLabel(labelName, labelColor, `Project: ${project.name}`);
        }
        // Update registry
        if (this.registry) {
            await this.registry.updateProject(project.id, {
                external: {
                    ...project.external,
                    github: {
                        ...project.external?.github,
                        labelName,
                        lastSynced: new Date().toISOString(),
                        syncError: undefined,
                    },
                },
            }, { emitEvent: false });
        }
        this.logger.info(`Synced project to GitHub: ${project.id}`);
    }
    // ============================================
    // GitHub API Operations
    // ============================================
    /**
     * Get label name for a project
     */
    getLabelName(projectId) {
        return `project:${projectId}`;
    }
    /**
     * Get environment with token
     */
    getGhEnv() {
        return this.token ? { ...process.env, GH_TOKEN: this.token } : process.env;
    }
    /**
     * Check if a label exists
     */
    async labelExists(labelName) {
        const result = await execFileNoThrow('gh', [
            'label',
            'list',
            '--repo',
            `${this.owner}/${this.repo}`,
            '--search',
            labelName,
            '--json',
            'name',
        ], { env: this.getGhEnv() });
        if (result.exitCode !== 0) {
            return false;
        }
        try {
            const labels = JSON.parse(result.stdout);
            return labels.some((l) => l.name === labelName);
        }
        catch {
            return false;
        }
    }
    /**
     * Create a label in GitHub
     */
    async createLabel(name, color, description) {
        const result = await execFileNoThrow('gh', [
            'label',
            'create',
            name,
            '--repo',
            `${this.owner}/${this.repo}`,
            '--color',
            color,
            '--description',
            description,
            '--force', // Update if exists
        ], { env: this.getGhEnv() });
        if (result.exitCode !== 0) {
            throw new Error(`Failed to create label: ${result.stderr}`);
        }
    }
    /**
     * Update a label in GitHub
     */
    async updateLabel(name, updates) {
        const args = [
            'label',
            'edit',
            name,
            '--repo',
            `${this.owner}/${this.repo}`,
        ];
        if (updates.name) {
            args.push('--name', updates.name);
        }
        if (updates.color) {
            args.push('--color', updates.color);
        }
        if (updates.description) {
            args.push('--description', updates.description);
        }
        const result = await execFileNoThrow('gh', args, { env: this.getGhEnv() });
        if (result.exitCode !== 0) {
            throw new Error(`Failed to update label: ${result.stderr}`);
        }
    }
    /**
     * Discover project labels from GitHub
     */
    async discoverProjects() {
        const result = await execFileNoThrow('gh', [
            'label',
            'list',
            '--repo',
            `${this.owner}/${this.repo}`,
            '--search',
            'project:',
            '--json',
            'name,description,color',
        ], { env: this.getGhEnv() });
        if (result.exitCode !== 0) {
            this.logger.error(`Failed to discover labels: ${result.stderr}`);
            return [];
        }
        try {
            const labels = JSON.parse(result.stdout);
            return labels
                .filter((l) => l.name.startsWith('project:'))
                .map((l) => {
                const id = l.name.replace('project:', '');
                const name = l.description?.replace('Project: ', '') || id;
                return {
                    id,
                    name,
                    external: {
                        github: {
                            labelName: l.name,
                            labelColor: l.color,
                        },
                    },
                };
            });
        }
        catch {
            return [];
        }
    }
}
//# sourceMappingURL=github-project-adapter.js.map