"use strict";
// Webhook Server for mycontext PM Integration
// Handles incoming webhook requests from mycontext PM
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebhookServer = void 0;
const http_1 = __importDefault(require("http"));
const https_1 = __importDefault(require("https"));
const events_1 = require("events");
const chalk_1 = __importDefault(require("chalk"));
class WebhookServer extends events_1.EventEmitter {
    constructor(config) {
        super();
        this.requestCounts = new Map();
        this.config = {
            host: "localhost",
            rateLimit: {
                windowMs: 15 * 60 * 1000, // 15 minutes
                maxRequests: 100,
            },
            ...config,
        };
    }
    // Start the webhook server
    async start() {
        return new Promise((resolve, reject) => {
            try {
                const serverOptions = this.config.ssl
                    ? {
                        key: this.config.ssl.key,
                        cert: this.config.ssl.cert,
                    }
                    : {};
                this.server = this.config.ssl
                    ? https_1.default.createServer(serverOptions, this.handleRequest.bind(this))
                    : http_1.default.createServer(this.handleRequest.bind(this));
                this.server.listen(this.config.port, this.config.host, () => {
                    const protocol = this.config.ssl ? "https" : "http";
                    const url = `${protocol}://${this.config.host}:${this.config.port}`;
                    console.log(chalk_1.default.green(`🚀 Webhook server started at ${url}`));
                    console.log(chalk_1.default.gray(`   Rate limit: ${this.config.rateLimit?.maxRequests} requests per ${this.config.rateLimit?.windowMs}ms`));
                    this.emit("started", url);
                    resolve();
                });
                this.server.on("error", (error) => {
                    console.error(chalk_1.default.red("❌ Webhook server error:"), error);
                    this.emit("error", error);
                    reject(error);
                });
            }
            catch (error) {
                reject(error);
            }
        });
    }
    // Stop the webhook server
    async stop() {
        return new Promise((resolve) => {
            if (this.server) {
                this.server.close(() => {
                    console.log(chalk_1.default.blue("🛑 Webhook server stopped"));
                    this.emit("stopped");
                    resolve();
                });
            }
            else {
                resolve();
            }
        });
    }
    // Handle incoming HTTP requests
    async handleRequest(req, res) {
        const clientIP = this.getClientIP(req);
        const now = Date.now();
        try {
            // Rate limiting
            if (!this.checkRateLimit(clientIP, now)) {
                this.sendError(res, 429, "Rate limit exceeded");
                return;
            }
            // Health check endpoint
            if (req.url === "/health" && req.method === "GET") {
                this.handleHealthCheck(res);
                return;
            }
            // Webhook endpoint
            if (req.url === "/webhook" && req.method === "POST") {
                await this.handleWebhook(req, res);
                return;
            }
            // Unknown endpoint
            this.sendError(res, 404, "Endpoint not found");
        }
        catch (error) {
            console.error(chalk_1.default.red("❌ Webhook request error:"), error);
            this.sendError(res, 500, "Internal server error");
        }
    }
    // Handle health check requests
    handleHealthCheck(res) {
        const healthData = {
            status: "healthy",
            timestamp: new Date().toISOString(),
            uptime: process.uptime(),
            version: process.version,
        };
        res.writeHead(200, { "Content-Type": "application/json" });
        res.end(JSON.stringify(healthData));
    }
    // Handle webhook requests
    async handleWebhook(req, res) {
        // Authenticate request
        if (!this.authenticateRequest(req)) {
            this.sendError(res, 401, "Unauthorized");
            return;
        }
        // Parse request body
        const body = await this.parseRequestBody(req);
        const event = body;
        // Validate webhook event
        if (!this.validateWebhookEvent(event)) {
            this.sendError(res, 400, "Invalid webhook event");
            return;
        }
        console.log(chalk_1.default.blue(`📨 Received webhook: ${event.type} for project ${event.projectId}`));
        // Emit event for processing
        this.emit("webhook_received", event);
        // Send success response
        res.writeHead(200, { "Content-Type": "application/json" });
        res.end(JSON.stringify({
            status: "received",
            eventId: event.timestamp,
            timestamp: new Date().toISOString(),
        }));
    }
    // Authenticate incoming requests
    authenticateRequest(req) {
        if (!this.config.authToken) {
            return true; // No auth required
        }
        const authHeader = req.headers.authorization;
        if (!authHeader) {
            return false;
        }
        const token = authHeader.replace("Bearer ", "");
        return token === this.config.authToken;
    }
    // Validate webhook event structure
    validateWebhookEvent(event) {
        return (event &&
            typeof event.type === "string" &&
            typeof event.projectId === "string" &&
            typeof event.timestamp === "string" &&
            event.data !== undefined);
    }
    // Parse JSON request body
    parseRequestBody(req) {
        return new Promise((resolve, reject) => {
            let body = "";
            req.on("data", (chunk) => {
                body += chunk.toString();
            });
            req.on("end", () => {
                try {
                    const data = JSON.parse(body);
                    resolve(data);
                }
                catch (error) {
                    reject(new Error("Invalid JSON"));
                }
            });
            req.on("error", reject);
        });
    }
    // Check rate limiting
    checkRateLimit(clientIP, now) {
        const rateLimit = this.config.rateLimit;
        if (!rateLimit)
            return true;
        const clientData = this.requestCounts.get(clientIP);
        if (!clientData || now > clientData.resetTime) {
            // Reset or new client
            this.requestCounts.set(clientIP, {
                count: 1,
                resetTime: now + rateLimit.windowMs,
            });
            return true;
        }
        if (clientData.count >= rateLimit.maxRequests) {
            return false;
        }
        clientData.count++;
        return true;
    }
    // Get client IP address
    getClientIP(req) {
        const forwarded = req.headers["x-forwarded-for"];
        if (forwarded && typeof forwarded === "string") {
            return forwarded.split(",")[0]?.trim() || "unknown";
        }
        const realIP = req.headers["x-real-ip"];
        if (realIP && typeof realIP === "string") {
            return realIP;
        }
        return req.socket?.remoteAddress || "unknown";
    }
    // Send error response
    sendError(res, statusCode, message) {
        res.writeHead(statusCode, { "Content-Type": "application/json" });
        res.end(JSON.stringify({
            error: message,
            timestamp: new Date().toISOString(),
        }));
    }
    // Get server status
    getStatus() {
        const running = this.server?.listening || false;
        const url = running
            ? `${this.config.ssl ? "https" : "http"}://${this.config.host}:${this.config.port}`
            : undefined;
        return {
            running,
            url,
            connections: this.requestCounts.size,
        };
    }
}
exports.WebhookServer = WebhookServer;
//# sourceMappingURL=WebhookServer.js.map