"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.gitEventHandler = exports.GitEventHandler = exports.GitEventType = void 0;
const simple_git_1 = __importDefault(require("simple-git"));
const events_1 = require("events");
const stateMachine_1 = require("./stateMachine");
const webhookService_1 = require("./webhookService");
const directus_client_1 = require("../../scripts/utils/directus-client");
// Import Directus SDK functions
const directusSdk = require('@directus/sdk');
const readItems = directusSdk.readItems;
const updateItem = directusSdk.updateItem;
/**
 * Git Event Types
 */
var GitEventType;
(function (GitEventType) {
    GitEventType["COMMIT"] = "commit";
    GitEventType["PUSH"] = "push";
    GitEventType["PULL"] = "pull";
    GitEventType["BRANCH_CREATE"] = "branch_create";
    GitEventType["BRANCH_DELETE"] = "branch_delete";
    GitEventType["BRANCH_CHECKOUT"] = "branch_checkout";
    GitEventType["MERGE"] = "merge";
    GitEventType["TAG"] = "tag";
    GitEventType["PR_OPEN"] = "pr_open";
    GitEventType["PR_CLOSE"] = "pr_close";
    GitEventType["PR_MERGE"] = "pr_merge";
})(GitEventType || (exports.GitEventType = GitEventType = {}));
/**
 * Git Event Handler Service
 * Integrates Git operations with task status workflow
 */
class GitEventHandler extends events_1.EventEmitter {
    constructor(repoPath = process.cwd()) {
        super();
        this.eventConfigs = [];
        this.taskMappings = new Map();
        this.watchInterval = null;
        this.lastCommitHash = '';
        this.repoPath = repoPath;
        this.git = (0, simple_git_1.default)(repoPath);
        this.initializeClient();
        this.loadEventConfigs();
        this.loadTaskMappings();
    }
    async initializeClient() {
        this.client = await (0, directus_client_1.getAuthenticatedClient)();
    }
    /**
     * Load event configurations from database or config
     */
    async loadEventConfigs() {
        // Default configurations
        this.eventConfigs = [
            {
                event: GitEventType.COMMIT,
                pattern: /^feat(\(.+\))?:/,
                action: 'START',
                conditions: {
                    branch: /^(feature|develop)/
                }
            },
            {
                event: GitEventType.COMMIT,
                pattern: /^fix(\(.+\))?:/,
                action: 'START',
                conditions: {
                    branch: /^(hotfix|bugfix)/
                }
            },
            {
                event: GitEventType.COMMIT,
                pattern: /^test(\(.+\))?:/,
                action: 'REVIEW',
                conditions: {
                    message: /\bpasses?\b/i
                }
            },
            {
                event: GitEventType.COMMIT,
                pattern: /^done(\(.+\))?:/,
                action: 'COMPLETE'
            },
            {
                event: GitEventType.PR_MERGE,
                action: 'APPROVE'
            },
            {
                event: GitEventType.BRANCH_DELETE,
                action: 'ARCHIVE',
                conditions: {
                    branch: /^feature\//
                }
            }
        ];
        // Load custom configurations from database if available
        if (this.client) {
            try {
                const customConfigs = await this.client.request(readItems('git_event_configs', {
                    filter: { active: { _eq: true } }
                }));
                if (customConfigs.length > 0) {
                    this.eventConfigs = [...this.eventConfigs, ...customConfigs];
                }
            }
            catch (error) {
                console.log('Using default Git event configurations');
            }
        }
    }
    /**
     * Load task-git mappings from database
     */
    async loadTaskMappings() {
        if (!this.client)
            await this.initializeClient();
        try {
            const mappings = await this.client.request(readItems('task_git_mappings', {
                filter: { active: { _eq: true } }
            }));
            mappings.forEach((mapping) => {
                this.taskMappings.set(mapping.taskId, mapping);
            });
        }
        catch (error) {
            console.log('No task-git mappings found');
        }
    }
    /**
     * Start watching for Git events
     */
    async startWatching(intervalMs = 5000) {
        console.log(`Starting Git event watcher (interval: ${intervalMs}ms)`);
        // Get initial commit hash
        const log = await this.git.log({ maxCount: 1 });
        this.lastCommitHash = log.latest?.hash || '';
        // Set up polling interval
        this.watchInterval = setInterval(async () => {
            await this.checkForNewCommits();
        }, intervalMs);
        // Also set up branch watching
        await this.setupBranchWatcher();
    }
    /**
     * Stop watching for Git events
     */
    stopWatching() {
        if (this.watchInterval) {
            clearInterval(this.watchInterval);
            this.watchInterval = null;
            console.log('Stopped Git event watcher');
        }
    }
    /**
     * Check for new commits
     */
    async checkForNewCommits() {
        try {
            const log = await this.git.log({ maxCount: 10 });
            if (log.latest && log.latest.hash !== this.lastCommitHash) {
                // Process new commits
                const newCommits = [];
                for (const commit of log.all) {
                    if (commit.hash === this.lastCommitHash)
                        break;
                    newCommits.push(commit);
                }
                // Process in reverse order (oldest first)
                for (const commit of newCommits.reverse()) {
                    await this.processCommit(commit);
                }
                this.lastCommitHash = log.latest.hash;
            }
        }
        catch (error) {
            console.error('Error checking for new commits:', error);
        }
    }
    /**
     * Process a Git commit
     */
    async processCommit(commit) {
        console.log(`Processing commit: ${commit.hash.substring(0, 7)} - ${commit.message}`);
        // Extract task ID from commit message
        const taskId = this.extractTaskId(commit.message);
        if (!taskId) {
            console.log('No task ID found in commit message');
            return;
        }
        // Find matching event configuration
        const config = this.findMatchingConfig(GitEventType.COMMIT, commit);
        if (!config) {
            console.log('No matching Git event configuration');
            return;
        }
        // Trigger status transition
        await this.triggerStatusTransition(taskId, config, commit);
    }
    /**
     * Extract task ID from commit message
     */
    extractTaskId(message) {
        // Look for patterns like [TASK-123], #123, or task:123
        const patterns = [
            /\[TASK-(\d+)\]/i,
            /#(\d+)/,
            /task:\s*(\d+)/i,
            /\(task\s+(\d+)\)/i
        ];
        for (const pattern of patterns) {
            const match = message.match(pattern);
            if (match) {
                return match[1];
            }
        }
        return null;
    }
    /**
     * Find matching event configuration
     */
    findMatchingConfig(eventType, data) {
        for (const config of this.eventConfigs) {
            if (config.event !== eventType)
                continue;
            // Check pattern
            if (config.pattern && !config.pattern.test(data.message))
                continue;
            // Check conditions
            if (config.conditions) {
                const currentBranch = data.branch || this.getCurrentBranch();
                if (config.conditions.branch) {
                    const branchPattern = config.conditions.branch instanceof RegExp
                        ? config.conditions.branch
                        : new RegExp(config.conditions.branch);
                    if (!branchPattern.test(currentBranch))
                        continue;
                }
                if (config.conditions.author && data.author !== config.conditions.author) {
                    continue;
                }
                if (config.conditions.message) {
                    if (!config.conditions.message.test(data.message))
                        continue;
                }
            }
            return config;
        }
        return null;
    }
    /**
     * Get current branch name
     */
    async getCurrentBranch() {
        try {
            const branch = await this.git.branch();
            return branch.current;
        }
        catch (error) {
            return 'main';
        }
    }
    /**
     * Trigger status transition based on Git event
     */
    async triggerStatusTransition(taskId, config, gitData) {
        try {
            // Create transition event
            const event = {
                type: config.action,
                metadata: {
                    ...config.metadata,
                    gitCommit: gitData.hash,
                    gitAuthor: gitData.author_name,
                    gitMessage: gitData.message,
                    gitBranch: await this.getCurrentBranch()
                }
            };
            // Trigger state machine transition
            await stateMachine_1.stateMachineService.transition(taskId, event);
            // Trigger webhooks
            await webhookService_1.webhookService.triggerWebhooks(`git.${config.event}`, taskId, {
                currentStatus: config.action.toLowerCase(),
                task: { id: taskId },
                metadata: event.metadata
            });
            // Emit event
            this.emit('statusTransition', {
                taskId,
                event: config.event,
                action: config.action,
                gitData
            });
            console.log(`Triggered ${config.action} for task ${taskId} based on Git ${config.event}`);
        }
        catch (error) {
            console.error(`Failed to trigger status transition: ${error}`);
        }
    }
    /**
     * Set up branch watcher
     */
    async setupBranchWatcher() {
        let currentBranch = await this.getCurrentBranch();
        setInterval(async () => {
            const newBranch = await this.getCurrentBranch();
            if (newBranch !== currentBranch) {
                await this.handleBranchChange(currentBranch, newBranch);
                currentBranch = newBranch;
            }
        }, 2000);
    }
    /**
     * Handle branch change
     */
    async handleBranchChange(oldBranch, newBranch) {
        console.log(`Branch changed: ${oldBranch} -> ${newBranch}`);
        // Extract task ID from branch name
        const taskId = this.extractTaskIdFromBranch(newBranch);
        if (!taskId)
            return;
        // Find configuration for branch checkout
        const config = this.eventConfigs.find(c => c.event === GitEventType.BRANCH_CHECKOUT);
        if (config) {
            await this.triggerStatusTransition(taskId, config, {
                oldBranch,
                newBranch
            });
        }
        this.emit('branchChange', { oldBranch, newBranch, taskId });
    }
    /**
     * Extract task ID from branch name
     */
    extractTaskIdFromBranch(branchName) {
        // Look for patterns like feature/TASK-123, task-123, etc.
        const patterns = [
            /TASK-(\d+)/i,
            /task-(\d+)/i,
            /^(\d+)-/
        ];
        for (const pattern of patterns) {
            const match = branchName.match(pattern);
            if (match) {
                return match[1];
            }
        }
        return null;
    }
    /**
     * Handle pull request events (webhook receiver)
     */
    async handlePullRequestEvent(event) {
        const { action, number, pull_request } = event;
        // Extract task ID from PR title or body
        const taskId = this.extractTaskId(pull_request.title) ||
            this.extractTaskId(pull_request.body);
        if (!taskId) {
            console.log('No task ID found in pull request');
            return;
        }
        let gitEventType;
        switch (action) {
            case 'opened':
                gitEventType = GitEventType.PR_OPEN;
                break;
            case 'closed':
                gitEventType = pull_request.merged
                    ? GitEventType.PR_MERGE
                    : GitEventType.PR_CLOSE;
                break;
            default:
                return;
        }
        const config = this.eventConfigs.find(c => c.event === gitEventType);
        if (config) {
            await this.triggerStatusTransition(taskId, config, {
                prNumber: number,
                prTitle: pull_request.title,
                prAuthor: pull_request.user.login,
                merged: pull_request.merged
            });
        }
    }
    /**
     * Create branch for task
     */
    async createTaskBranch(taskId, taskTitle) {
        const branchName = `task-${taskId}-${taskTitle.toLowerCase().replace(/\s+/g, '-').substring(0, 30)}`;
        try {
            await this.git.checkoutLocalBranch(branchName);
            // Store mapping
            this.taskMappings.set(taskId, {
                taskId,
                branch: branchName
            });
            // Save to database
            if (this.client) {
                await this.client.request(createItem('task_git_mappings', {
                    task_id: taskId,
                    branch: branchName,
                    created_at: new Date().toISOString()
                }));
            }
            console.log(`Created branch ${branchName} for task ${taskId}`);
            return branchName;
        }
        catch (error) {
            throw new Error(`Failed to create branch: ${error.message}`);
        }
    }
    /**
     * Get Git information for task
     */
    async getTaskGitInfo(taskId) {
        const mapping = this.taskMappings.get(taskId);
        if (!mapping || !mapping.branch) {
            return null;
        }
        try {
            // Get commits for this task
            const log = await this.git.log({
                '--grep': taskId
            });
            // Get branch info
            const branches = await this.git.branch();
            const branchExists = branches.all.includes(mapping.branch);
            return {
                branch: mapping.branch,
                branchExists,
                commits: log.all,
                commitCount: log.total,
                lastCommit: log.latest
            };
        }
        catch (error) {
            console.error('Failed to get Git info:', error);
            return null;
        }
    }
    /**
     * Clean up service
     */
    async cleanup() {
        this.stopWatching();
        this.removeAllListeners();
        this.taskMappings.clear();
    }
}
exports.GitEventHandler = GitEventHandler;
// Export singleton instance
exports.gitEventHandler = new GitEventHandler();
//# sourceMappingURL=gitEventHandler.js.map