#!/usr/bin/env python3
"""
Genesis Webhook Receiver
=========================
HTTP webhook receiver for external integrations.

PM-046: Webhook Receiver
- Receives GHL, Instantly, Telnyx webhooks
- Routes to appropriate skill handlers
- Logs all webhook events
"""

import os
import json
import hmac
import hashlib
import logging
from typing import Dict, Any, List, Optional, Callable
from datetime import datetime
from dataclasses import dataclass, asdict

# Optional Flask import
try:
    from flask import Flask, request, jsonify
    FLASK_AVAILABLE = True
except ImportError:
    FLASK_AVAILABLE = False

logger = logging.getLogger("WebhookReceiver")
logging.basicConfig(level=logging.INFO)


@dataclass
class WebhookEvent:
    """Represents a received webhook event."""
    event_id: str
    source: str
    event_type: str
    payload: Dict[str, Any]
    headers: Dict[str, str]
    received_at: str
    processed: bool
    processing_result: Optional[str]

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


class WebhookReceiver:
    """
    Webhook receiver for external integrations.
    Routes webhooks to appropriate handlers based on source and event type.
    """

    # Webhook signature headers by source
    SIGNATURE_HEADERS = {
        "ghl": "X-GHL-Signature",
        "instantly": "X-Instantly-Signature",
        "telnyx": "telnyx-signature-ed25519",
        "stripe": "Stripe-Signature",
        "slack": "X-Slack-Signature"
    }

    def __init__(self, secret_keys: Dict[str, str] = None):
        """
        Initialize webhook receiver.

        Args:
            secret_keys: Dict mapping source to secret key for signature verification
        """
        self.secret_keys = secret_keys or {}

        # Load from environment if not provided
        self.secret_keys.setdefault("ghl", os.getenv("GHL_WEBHOOK_SECRET", ""))
        self.secret_keys.setdefault("instantly", os.getenv("INSTANTLY_WEBHOOK_SECRET", ""))
        self.secret_keys.setdefault("telnyx", os.getenv("TELNYX_WEBHOOK_SECRET", ""))

        # Event handlers registry
        self.handlers: Dict[str, Dict[str, Callable]] = {
            "ghl": {},
            "instantly": {},
            "telnyx": {},
            "generic": {}
        }

        # Event log
        self.event_log: List[WebhookEvent] = []

        logger.info("Webhook Receiver initialized")

    def _generate_event_id(self) -> str:
        """Generate unique event ID."""
        import uuid
        return f"wh_{uuid.uuid4().hex[:12]}"

    def _verify_signature(self, source: str, payload: bytes,
                          signature: str) -> bool:
        """
        Verify webhook signature.

        Args:
            source: Webhook source (ghl, instantly, telnyx)
            payload: Raw request body
            signature: Signature from header

        Returns:
            True if signature is valid
        """
        secret = self.secret_keys.get(source)
        if not secret:
            logger.warning(f"No secret key configured for {source}, skipping verification")
            return True

        # Compute HMAC-SHA256
        expected = hmac.new(
            secret.encode(),
            payload,
            hashlib.sha256
        ).hexdigest()

        return hmac.compare_digest(expected, signature)

    def register_handler(self, source: str, event_type: str,
                         handler: Callable) -> None:
        """
        Register a handler for a specific webhook event.

        Args:
            source: Webhook source (ghl, instantly, telnyx, generic)
            event_type: Event type to handle
            handler: Handler function
        """
        if source not in self.handlers:
            self.handlers[source] = {}

        self.handlers[source][event_type] = handler
        logger.info(f"Registered handler for {source}:{event_type}")

    def receive(self, source: str, payload: Dict[str, Any],
                headers: Dict[str, str] = None,
                raw_body: bytes = None) -> Dict[str, Any]:
        """
        Receive and process a webhook event.

        Args:
            source: Webhook source
            payload: Parsed payload (dict)
            headers: Request headers
            raw_body: Raw request body for signature verification

        Returns:
            Processing result
        """
        headers = headers or {}
        event_id = self._generate_event_id()

        # Verify signature if raw body provided
        if raw_body and source in self.SIGNATURE_HEADERS:
            sig_header = self.SIGNATURE_HEADERS[source]
            signature = headers.get(sig_header, "")

            if not self._verify_signature(source, raw_body, signature):
                logger.warning(f"Invalid signature for webhook from {source}")
                return {"error": "invalid_signature", "event_id": event_id}

        # Extract event type
        event_type = self._extract_event_type(source, payload)

        # Create event record
        event = WebhookEvent(
            event_id=event_id,
            source=source,
            event_type=event_type,
            payload=payload,
            headers=headers,
            received_at=datetime.utcnow().isoformat(),
            processed=False,
            processing_result=None
        )

        # Log event
        self.event_log.append(event)
        logger.info(f"Received webhook: {source}:{event_type} (ID: {event_id})")

        # Route to handler
        result = self._route_to_handler(source, event_type, payload)

        # Update event record
        event.processed = True
        event.processing_result = result.get("status", "processed")

        return {
            "event_id": event_id,
            "status": "received",
            "event_type": event_type,
            "result": result
        }

    def _extract_event_type(self, source: str, payload: Dict[str, Any]) -> str:
        """Extract event type from payload based on source."""
        if source == "ghl":
            return payload.get("event", payload.get("type", "unknown"))
        elif source == "instantly":
            return payload.get("event_type", payload.get("event", "unknown"))
        elif source == "telnyx":
            return payload.get("data", {}).get("event_type", "unknown")
        else:
            return payload.get("event_type", payload.get("event", payload.get("type", "unknown")))

    def _route_to_handler(self, source: str, event_type: str,
                          payload: Dict[str, Any]) -> Dict[str, Any]:
        """Route event to appropriate handler."""
        # Check source-specific handler
        if source in self.handlers and event_type in self.handlers[source]:
            try:
                return self.handlers[source][event_type](payload)
            except Exception as e:
                logger.error(f"Handler error for {source}:{event_type}: {e}")
                return {"status": "error", "message": str(e)}

        # Check generic handler
        if event_type in self.handlers.get("generic", {}):
            try:
                return self.handlers["generic"][event_type](payload)
            except Exception as e:
                logger.error(f"Generic handler error for {event_type}: {e}")
                return {"status": "error", "message": str(e)}

        # Default: just acknowledge
        logger.info(f"No handler for {source}:{event_type}, acknowledging")
        return {"status": "acknowledged", "message": "No handler registered"}

    # ===== GHL WEBHOOK HANDLERS =====

    def handle_ghl_contact_created(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Handle GHL contact.created event."""
        contact = payload.get("contact", payload)
        logger.info(f"GHL Contact Created: {contact.get('email', 'unknown')}")

        return {
            "status": "processed",
            "action": "contact_created",
            "contact_id": contact.get("id")
        }

    def handle_ghl_opportunity_updated(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Handle GHL opportunity status change."""
        opportunity = payload.get("opportunity", payload)
        logger.info(f"GHL Opportunity Updated: {opportunity.get('id')}")

        return {
            "status": "processed",
            "action": "opportunity_updated",
            "opportunity_id": opportunity.get("id"),
            "stage": opportunity.get("stage")
        }

    # ===== INSTANTLY WEBHOOK HANDLERS =====

    def handle_instantly_email_opened(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Handle Instantly email opened event."""
        email = payload.get("email", "unknown")
        campaign_id = payload.get("campaign_id", "unknown")
        logger.info(f"Instantly Email Opened: {email} (Campaign: {campaign_id})")

        return {
            "status": "processed",
            "action": "email_opened",
            "email": email,
            "campaign_id": campaign_id
        }

    def handle_instantly_reply_received(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Handle Instantly reply received event."""
        email = payload.get("email", "unknown")
        logger.info(f"Instantly Reply Received: {email}")

        return {
            "status": "processed",
            "action": "reply_received",
            "email": email,
            "reply_text": payload.get("reply_text", "")[:100]
        }

    # ===== TELNYX WEBHOOK HANDLERS =====

    def handle_telnyx_call_initiated(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Handle Telnyx call.initiated event."""
        data = payload.get("data", {}).get("payload", {})
        call_id = data.get("call_control_id", "unknown")
        logger.info(f"Telnyx Call Initiated: {call_id}")

        return {
            "status": "processed",
            "action": "call_initiated",
            "call_control_id": call_id,
            "from": data.get("from"),
            "to": data.get("to")
        }

    def handle_telnyx_call_answered(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Handle Telnyx call.answered event."""
        data = payload.get("data", {}).get("payload", {})
        call_id = data.get("call_control_id", "unknown")
        logger.info(f"Telnyx Call Answered: {call_id}")

        return {
            "status": "processed",
            "action": "call_answered",
            "call_control_id": call_id
        }

    def handle_telnyx_call_hangup(self, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Handle Telnyx call.hangup event."""
        data = payload.get("data", {}).get("payload", {})
        call_id = data.get("call_control_id", "unknown")
        logger.info(f"Telnyx Call Hangup: {call_id}")

        return {
            "status": "processed",
            "action": "call_hangup",
            "call_control_id": call_id,
            "duration_seconds": data.get("call_leg_end_reason", {}).get("duration_seconds", 0)
        }

    def setup_default_handlers(self) -> None:
        """Register default handlers for common events."""
        # GHL handlers
        self.register_handler("ghl", "contact.created", self.handle_ghl_contact_created)
        self.register_handler("ghl", "ContactCreate", self.handle_ghl_contact_created)
        self.register_handler("ghl", "opportunity.updated", self.handle_ghl_opportunity_updated)
        self.register_handler("ghl", "OpportunityUpdate", self.handle_ghl_opportunity_updated)

        # Instantly handlers
        self.register_handler("instantly", "email_opened", self.handle_instantly_email_opened)
        self.register_handler("instantly", "reply_received", self.handle_instantly_reply_received)

        # Telnyx handlers
        self.register_handler("telnyx", "call.initiated", self.handle_telnyx_call_initiated)
        self.register_handler("telnyx", "call.answered", self.handle_telnyx_call_answered)
        self.register_handler("telnyx", "call.hangup", self.handle_telnyx_call_hangup)

        logger.info("Default webhook handlers registered")

    def get_event_log(self, source: str = None, limit: int = 100) -> List[Dict[str, Any]]:
        """Get event log, optionally filtered by source."""
        events = self.event_log[-limit:]
        if source:
            events = [e for e in events if e.source == source]
        return [e.to_dict() for e in events]

    def get_metrics(self) -> Dict[str, Any]:
        """Get webhook receiver metrics."""
        if not self.event_log:
            return {"total_events": 0}

        by_source = {}
        by_type = {}
        processed_count = 0

        for event in self.event_log:
            by_source[event.source] = by_source.get(event.source, 0) + 1
            by_type[event.event_type] = by_type.get(event.event_type, 0) + 1
            if event.processed:
                processed_count += 1

        return {
            "total_events": len(self.event_log),
            "processed": processed_count,
            "by_source": by_source,
            "by_type": by_type,
            "handlers_registered": sum(len(h) for h in self.handlers.values())
        }

    # ===== FLASK APP FACTORY =====

    def create_flask_app(self) -> Optional[Any]:
        """Create Flask app with webhook endpoints."""
        if not FLASK_AVAILABLE:
            logger.warning("Flask not available.")
            return None

        app = Flask(__name__)
        receiver = self

        @app.route("/webhooks/<source>", methods=["POST"])
        def webhook_handler(source):
            payload = request.get_json(force=True)
            headers = dict(request.headers)
            raw_body = request.get_data()

            result = receiver.receive(source, payload, headers, raw_body)
            return jsonify(result)

        @app.route("/webhooks/health", methods=["GET"])
        def webhook_health():
            return jsonify({
                "status": "healthy",
                "metrics": receiver.get_metrics()
            })

        return app


# Global instance
_receiver: Optional[WebhookReceiver] = None


def get_receiver() -> WebhookReceiver:
    """Get or create global webhook receiver."""
    global _receiver
    if _receiver is None:
        _receiver = WebhookReceiver()
        _receiver.setup_default_handlers()
    return _receiver


if __name__ == "__main__":
    # Self-test
    receiver = WebhookReceiver()
    receiver.setup_default_handlers()

    print("\n=== Webhook Receiver Test ===")

    # Test GHL webhook
    ghl_payload = {
        "event": "contact.created",
        "contact": {
            "id": "con_123",
            "email": "test@example.com",
            "first_name": "Test",
            "last_name": "Contact"
        }
    }
    result = receiver.receive("ghl", ghl_payload)
    print(f"GHL Webhook: {result}")

    # Test Telnyx webhook
    telnyx_payload = {
        "data": {
            "event_type": "call.initiated",
            "payload": {
                "call_control_id": "call_xyz123",
                "from": "+15551234567",
                "to": "+15559876543"
            }
        }
    }
    result = receiver.receive("telnyx", telnyx_payload)
    print(f"Telnyx Webhook: {result}")

    # Test Instantly webhook
    instantly_payload = {
        "event_type": "email_opened",
        "email": "lead@example.com",
        "campaign_id": "camp_abc"
    }
    result = receiver.receive("instantly", instantly_payload)
    print(f"Instantly Webhook: {result}")

    # Metrics
    print(f"\nMetrics: {receiver.get_metrics()}")
