"use strict";
// PM Integration Service
// Handles communication between mycontext PM and MyContext CLI
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PMIntegrationService = void 0;
const fs_1 = __importDefault(require("fs"));
const https_1 = __importDefault(require("https"));
const http_1 = __importDefault(require("http"));
const events_1 = require("events");
const chalk_1 = __importDefault(require("chalk"));
const pm_integration_1 = require("../types/pm-integration");
class PMIntegrationService extends events_1.EventEmitter {
    constructor(config = {}) {
        super();
        this.isConnected = false;
        this.lastSyncTimestamp = "";
        this.config = {
            syncInterval: 60, // 1 hour default
            retryAttempts: 3,
            timeout: 30000, // 30 seconds
            enableRealTimeSync: true,
            ...config,
        };
        this.setupEventHandlers();
    }
    setupEventHandlers() {
        this.on("webhook_received", this.handleWebhookEvent.bind(this));
        this.on("sync_error", this.handleSyncError.bind(this));
        this.on("connection_lost", this.handleConnectionLost.bind(this));
    }
    // Initialize the integration service
    async initialize() {
        try {
            console.log(chalk_1.default.blue("🔗 Initializing mycontext PM Integration..."));
            if (!this.config.pmEndpoint && !this.config.webhookUrl) {
                throw new pm_integration_1.PMIntegrationError("PM endpoint or webhook URL is required", pm_integration_1.PMIntegrationErrorCodes.INVALID_PROJECT_ID);
            }
            // Test connection
            await this.testConnection();
            this.isConnected = true;
            // Start sync timer if enabled
            if (this.config.enableRealTimeSync && this.config.syncInterval) {
                this.startSyncTimer();
            }
            console.log(chalk_1.default.green("✅ PM Integration initialized successfully"));
            this.emit("initialized", this.getStatus());
        }
        catch (error) {
            console.error(chalk_1.default.red("❌ Failed to initialize PM integration:"), error);
            this.emit("initialization_failed", error);
            throw error;
        }
    }
    // Test connection to PM service
    async testConnection() {
        if (!this.config.pmEndpoint)
            return;
        return new Promise((resolve, reject) => {
            const url = new URL(`${this.config.pmEndpoint}/health`);
            const options = {
                method: "GET",
                headers: this.getAuthHeaders(),
                timeout: this.config.timeout,
            };
            const req = (url.protocol === "https:" ? https_1.default : http_1.default).request(url, options, (res) => {
                if (res.statusCode === 200) {
                    resolve();
                }
                else {
                    reject(new Error(`Health check failed: ${res.statusCode}`));
                }
            });
            req.on("error", reject);
            req.on("timeout", () => {
                req.destroy();
                reject(new Error("Connection timeout"));
            });
            req.end();
        });
    }
    // Import project plan from PM
    async importProjectPlan(planPath) {
        try {
            console.log(chalk_1.default.blue(`📥 Importing project plan from ${planPath}...`));
            if (!fs_1.default.existsSync(planPath)) {
                throw new pm_integration_1.PMIntegrationError(`Project plan file not found: ${planPath}`, pm_integration_1.PMIntegrationErrorCodes.INVALID_PLAN_FORMAT);
            }
            const planData = fs_1.default.readFileSync(planPath, "utf-8");
            const plan = JSON.parse(planData);
            // Validate plan structure
            await this.validateProjectPlan(plan);
            console.log(chalk_1.default.green(`✅ Project plan imported: ${plan.project.name}`));
            this.emit("plan_imported", plan);
            return plan;
        }
        catch (error) {
            console.error(chalk_1.default.red("❌ Failed to import project plan:"), error);
            this.emit("plan_import_failed", error);
            throw error;
        }
    }
    // Export progress to PM
    async exportProgress(output, webhookUrl) {
        const targetUrl = webhookUrl || this.config.webhookUrl;
        if (!targetUrl) {
            throw new pm_integration_1.PMIntegrationError("No webhook URL configured for progress export", pm_integration_1.PMIntegrationErrorCodes.SYNC_FAILED);
        }
        try {
            console.log(chalk_1.default.blue("📤 Exporting progress to mycontext PM..."));
            await this.sendWebhook(targetUrl, {
                type: "progress_update",
                projectId: output.projectId,
                data: output,
                timestamp: new Date().toISOString(),
            });
            this.lastSyncTimestamp = new Date().toISOString();
            console.log(chalk_1.default.green("✅ Progress exported successfully"));
            this.emit("progress_exported", output);
        }
        catch (error) {
            console.error(chalk_1.default.red("❌ Failed to export progress:"), error);
            this.emit("progress_export_failed", error);
            throw error;
        }
    }
    // Sync progress with PM (bidirectional)
    async syncProgress(request) {
        if (!this.config.pmEndpoint) {
            throw new pm_integration_1.PMIntegrationError("PM endpoint not configured", pm_integration_1.PMIntegrationErrorCodes.SYNC_FAILED);
        }
        try {
            console.log(chalk_1.default.blue("🔄 Syncing progress with mycontext PM..."));
            const response = await this.makeAuthenticatedRequest(`${this.config.pmEndpoint}/api/sync`, "POST", request);
            this.lastSyncTimestamp = new Date().toISOString();
            console.log(chalk_1.default.green("✅ Progress sync completed"));
            this.emit("progress_synced", response);
            return response;
        }
        catch (error) {
            console.error(chalk_1.default.red("❌ Progress sync failed:"), error);
            this.emit("progress_sync_failed", error);
            throw error;
        }
    }
    // Send webhook with retry logic
    async sendWebhook(url, event, attempt = 1) {
        return new Promise((resolve, reject) => {
            const payload = JSON.stringify(event);
            const options = {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "Content-Length": Buffer.byteLength(payload),
                    ...this.getAuthHeaders(),
                },
                timeout: this.config.timeout,
            };
            const req = (url.startsWith("https:") ? https_1.default : http_1.default).request(url, options, (res) => {
                let data = "";
                res.on("data", (chunk) => {
                    data += chunk;
                });
                res.on("end", () => {
                    if (res.statusCode &&
                        res.statusCode >= 200 &&
                        res.statusCode < 300) {
                        resolve();
                    }
                    else {
                        reject(new Error(`Webhook failed: ${res.statusCode} ${data}`));
                    }
                });
            });
            req.on("error", reject);
            req.on("timeout", () => {
                req.destroy();
                reject(new Error("Webhook timeout"));
            });
            req.write(payload);
            req.end();
        });
    }
    // Make authenticated HTTP request
    async makeAuthenticatedRequest(url, method = "GET", data) {
        return new Promise((resolve, reject) => {
            const payload = data ? JSON.stringify(data) : undefined;
            const options = {
                method,
                headers: {
                    "Content-Type": "application/json",
                    ...this.getAuthHeaders(),
                    ...(payload && { "Content-Length": Buffer.byteLength(payload) }),
                },
                timeout: this.config.timeout,
            };
            const req = (url.startsWith("https:") ? https_1.default : http_1.default).request(url, options, (res) => {
                let responseData = "";
                res.on("data", (chunk) => {
                    responseData += chunk;
                });
                res.on("end", () => {
                    try {
                        if (res.statusCode &&
                            res.statusCode >= 200 &&
                            res.statusCode < 300) {
                            const result = responseData ? JSON.parse(responseData) : {};
                            resolve(result);
                        }
                        else {
                            reject(new Error(`Request failed: ${res.statusCode} ${responseData}`));
                        }
                    }
                    catch (error) {
                        reject(new Error(`Invalid response: ${error}`));
                    }
                });
            });
            req.on("error", reject);
            req.on("timeout", () => {
                req.destroy();
                reject(new Error("Request timeout"));
            });
            if (payload) {
                req.write(payload);
            }
            req.end();
        });
    }
    // Get authentication headers
    getAuthHeaders() {
        const headers = {};
        if (this.config.apiKey) {
            headers["Authorization"] = `Bearer ${this.config.apiKey}`;
        }
        return headers;
    }
    // Validate project plan structure
    async validateProjectPlan(plan) {
        if (!plan.project || !plan.project.name || !plan.project.description) {
            throw new pm_integration_1.PMIntegrationError("Invalid project plan: missing required project fields", pm_integration_1.PMIntegrationErrorCodes.PLAN_VALIDATION_FAILED);
        }
        if (!plan.myContext || !plan.myContext.framework) {
            throw new pm_integration_1.PMIntegrationError("Invalid project plan: missing MyContext configuration", pm_integration_1.PMIntegrationErrorCodes.PLAN_VALIDATION_FAILED);
        }
        // Validate framework compatibility
        const supportedFrameworks = ["nextjs", "react", "vue", "angular"];
        if (!supportedFrameworks.includes(plan.myContext.framework)) {
            throw new pm_integration_1.PMIntegrationError(`Unsupported framework: ${plan.myContext.framework}`, pm_integration_1.PMIntegrationErrorCodes.PLAN_VALIDATION_FAILED);
        }
        // Validate architecture compatibility
        const supportedArchitectures = [
            "nextjs-app-router",
            "nextjs-pages",
            "react-spa",
        ];
        if (plan.myContext.architecture &&
            !supportedArchitectures.includes(plan.myContext.architecture)) {
            throw new pm_integration_1.PMIntegrationError(`Unsupported architecture: ${plan.myContext.architecture}`, pm_integration_1.PMIntegrationErrorCodes.PLAN_VALIDATION_FAILED);
        }
    }
    // Start periodic sync timer
    startSyncTimer() {
        if (!this.config.syncInterval)
            return;
        const intervalMs = this.config.syncInterval * 60 * 1000; // Convert minutes to milliseconds
        this.syncTimer = setInterval(async () => {
            try {
                if (this.config.projectId) {
                    await this.syncProgress({
                        projectId: this.config.projectId,
                        syncTimestamp: this.lastSyncTimestamp,
                        includeTasks: true,
                        includeComponents: true,
                        includeMetrics: true,
                    });
                }
            }
            catch (error) {
                console.warn(chalk_1.default.yellow("⚠️  Periodic sync failed:"), error);
            }
        }, intervalMs);
        console.log(chalk_1.default.gray(`⏰ Periodic sync enabled (${this.config.syncInterval} minutes)`));
    }
    // Event handlers
    handleWebhookEvent(event) {
        console.log(chalk_1.default.blue(`📨 Received webhook event: ${event.type}`));
        switch (event.type) {
            case "sync_request":
                this.handleSyncRequest(event);
                break;
            case "error":
                this.handleRemoteError(event);
                break;
            default:
                this.emit("webhook_event", event);
        }
    }
    handleSyncRequest(event) {
        // Respond to sync requests from PM
        if (event.data.requestId) {
            this.emit("sync_requested", event);
        }
    }
    handleRemoteError(event) {
        console.error(chalk_1.default.red("🚨 Remote PM error:"), event.data);
        this.emit("remote_error", event.data);
    }
    handleSyncError(error) {
        console.error(chalk_1.default.red("🔄 Sync error:"), error);
        this.isConnected = false;
        this.emit("connection_lost");
    }
    handleConnectionLost() {
        console.warn(chalk_1.default.yellow("⚠️  Connection to PM lost, attempting to reconnect..."));
        // Attempt to reconnect after a delay
        setTimeout(async () => {
            try {
                await this.initialize();
            }
            catch (error) {
                console.error(chalk_1.default.red("❌ Reconnection failed:"), error);
            }
        }, 5000);
    }
    // Public API methods
    getStatus() {
        return {
            connected: this.isConnected,
            lastSync: this.lastSyncTimestamp,
            projectId: this.config.projectId,
            pendingUpdates: 0,
        };
    }
    updateConfig(newConfig) {
        this.config = { ...this.config, ...newConfig };
        // Restart sync timer if interval changed
        if (this.syncTimer) {
            clearInterval(this.syncTimer);
            this.startSyncTimer();
        }
    }
    disconnect() {
        if (this.syncTimer) {
            clearInterval(this.syncTimer);
        }
        this.isConnected = false;
        this.emit("disconnected");
    }
    // Utility method to create integration from config file
    static fromConfigFile(configPath) {
        if (!fs_1.default.existsSync(configPath)) {
            throw new Error(`Config file not found: ${configPath}`);
        }
        const configData = fs_1.default.readFileSync(configPath, "utf-8");
        const config = JSON.parse(configData);
        return new PMIntegrationService(config);
    }
}
exports.PMIntegrationService = PMIntegrationService;
//# sourceMappingURL=PMIntegrationService.js.map