/**
 * CI/CD Workflow Monitor
 *
 * Monitors GitHub Actions workflows for failures by polling the GitHub API
 * every 60 seconds. Uses conditional requests and rate limit handling for
 * efficient API usage.
 */
import { Octokit } from '@octokit/rest';
import { StateManager } from './state-manager.js';
import { consoleLogger } from '../../utils/logger.js';
/**
 * WorkflowMonitor - Polls GitHub Actions for workflow failures
 *
 * Features:
 * - Polls every 60 seconds (configurable)
 * - Detects failed workflow runs within 2 minutes
 * - Uses conditional requests (If-Modified-Since) to reduce API calls
 * - Handles rate limiting with exponential backoff
 * - Deduplicates failures via StateManager
 * - Extracts comprehensive workflow metadata
 */
export class WorkflowMonitor {
    /**
     * Create workflow monitor
     *
     * @param config - Monitor configuration
     * @param stateManager - State manager instance (optional, creates new if not provided)
     */
    constructor(config, stateManager = new StateManager()) {
        this.pollingTimer = null;
        this.lastModified = null;
        this.isPolling = false;
        // Validate config
        if (!config.token) {
            throw new Error('GitHub token is required');
        }
        if (!config.owner || !config.repo) {
            throw new Error('Repository owner and name are required');
        }
        this.config = {
            token: config.token,
            owner: config.owner,
            repo: config.repo,
            pollInterval: config.pollInterval ?? 60000, // Default: 60s
            debug: config.debug ?? false
        };
        this.logger = config.logger ?? consoleLogger;
        this.octokit = new Octokit({ auth: config.token });
        this.stateManager = stateManager;
    }
    /**
     * Start monitoring (begins polling)
     */
    start() {
        if (this.isPolling) {
            this.log('Monitor already running');
            return;
        }
        this.isPolling = true;
        this.log('Starting monitor...');
        // Initial poll + schedule recurring polls
        const runPoll = () => {
            this.poll().catch((error) => console.error('Poll failed:', error));
        };
        runPoll();
        this.pollingTimer = setInterval(runPoll, this.config.pollInterval);
        this.log(`Monitor started (polling every ${this.config.pollInterval}ms)`);
    }
    /**
     * Stop monitoring (stops polling)
     */
    stop() {
        if (!this.isPolling) {
            this.log('Monitor not running');
            return;
        }
        if (this.pollingTimer) {
            clearInterval(this.pollingTimer);
            this.pollingTimer = null;
        }
        this.isPolling = false;
        this.log('Monitor stopped');
    }
    /**
     * Check if monitor is running
     */
    isRunning() {
        return this.isPolling;
    }
    /**
     * Poll GitHub Actions API for workflow runs
     *
     * @returns Poll result with statistics
     */
    async poll() {
        this.log('Polling GitHub Actions API...');
        try {
            // Build request headers
            const headers = {};
            if (this.lastModified) {
                headers['If-Modified-Since'] = this.lastModified;
            }
            // Fetch workflow runs
            const response = await this.octokit.rest.actions.listWorkflowRunsForRepo({
                owner: this.config.owner,
                repo: this.config.repo,
                status: 'completed', // Only completed runs
                per_page: 100, // Max per page
                headers
            });
            // Extract rate limit info
            const rateLimitRemaining = parseInt(response.headers['x-ratelimit-remaining'] || '0', 10);
            this.log(`Rate limit remaining: ${rateLimitRemaining}`);
            // Handle 304 Not Modified (no new runs)
            // Note: Octokit types response.status as 200, but API can return 304
            if (response.status === 304) {
                this.log('No new workflow runs (304 Not Modified)');
                return {
                    totalRuns: 0,
                    newFailures: 0,
                    duplicates: 0,
                    statusCode: 304,
                    rateLimitRemaining
                };
            }
            // Update Last-Modified header for next request
            if (response.headers['last-modified']) {
                this.lastModified = response.headers['last-modified'];
            }
            // Filter failed runs
            const failedRuns = response.data.workflow_runs.filter((run) => run.conclusion === 'failure');
            this.log(`Found ${failedRuns.length} failed runs (of ${response.data.workflow_runs.length} total)`);
            // Load state once to check for duplicates
            const state = await this.stateManager.loadState();
            let newFailures = 0;
            let duplicates = 0;
            for (const run of failedRuns) {
                if (state.failures[run.id]) {
                    duplicates++;
                    continue;
                }
                await this.stateManager.addFailure(this.extractFailureRecord(run));
                newFailures++;
                this.log(`New failure detected: ${run.name} (run #${run.run_number})`);
            }
            // Update last poll timestamp
            await this.stateManager.updateLastPoll();
            return {
                totalRuns: response.data.workflow_runs.length,
                newFailures,
                duplicates,
                statusCode: response.status,
                rateLimitRemaining
            };
        }
        catch (error) {
            // Handle rate limiting (429)
            if (error.status === 429) {
                const retryAfter = parseInt(error.response?.headers['retry-after'] || '60', 10);
                console.warn(`Rate limited! Retry after ${retryAfter}s`);
                // Exponential backoff
                await this.sleep(retryAfter * 1000);
                // Retry poll
                return this.poll();
            }
            // Handle other errors
            this.logger.error('Poll error:', error.message);
            return {
                totalRuns: 0,
                newFailures: 0,
                duplicates: 0,
                statusCode: error.status || 500,
                rateLimitRemaining: 0
            };
        }
    }
    /**
     * Extract failure record from GitHub workflow run
     *
     * @param run - GitHub workflow run data
     * @returns Failure record for state storage
     */
    extractFailureRecord(run) {
        return {
            runId: run.id,
            workflowName: run.name,
            commitSha: run.head_sha,
            branch: run.head_branch,
            detectedAt: new Date().toISOString(),
            processed: false,
            analyzed: false,
            fixed: false,
            url: run.html_url
        };
    }
    /**
     * Sleep for specified milliseconds
     */
    sleep(ms) {
        return new Promise((resolve) => setTimeout(resolve, ms));
    }
    /**
     * Debug logging
     */
    log(message) {
        if (this.config.debug) {
            console.log(`[WorkflowMonitor] ${message}`);
        }
    }
}
//# sourceMappingURL=workflow-monitor.js.map