#!/usr/bin/env python3
"""
GitHub Webhook Listener for ThrillWiki CI/CD
This script listens for GitHub webhook events and triggers deployments to a Linux VM.
"""

import os
import sys
import json
import hmac
import hashlib
import logging
import subprocess
from http.server import HTTPServer, BaseHTTPRequestHandler
import threading
from datetime import datetime

# Configuration
WEBHOOK_PORT = int(os.environ.get("WEBHOOK_PORT", 9000))
WEBHOOK_SECRET = os.environ.get("WEBHOOK_SECRET", "")
WEBHOOK_ENABLED = os.environ.get("WEBHOOK_ENABLED", "true").lower() == "true"
VM_HOST = os.environ.get("VM_HOST", "localhost")
VM_PORT = int(os.environ.get("VM_PORT", 22))
VM_USER = os.environ.get("VM_USER", "ubuntu")
VM_KEY_PATH = os.environ.get("VM_KEY_PATH", "~/.ssh/***REMOVED***")
PROJECT_PATH = os.environ.get("VM_PROJECT_PATH", "/home/ubuntu/thrillwiki")
REPO_URL = os.environ.get(
    "REPO_URL",
    "https://github.com/YOUR_USERNAME/thrillwiki_django_no_react.git",
)
DEPLOY_BRANCH = os.environ.get("DEPLOY_BRANCH", "main")

# GitHub API Configuration
GITHUB_USERNAME = os.environ.get("GITHUB_USERNAME", "")
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN", "")
GITHUB_API_ENABLED = os.environ.get("GITHUB_API_ENABLED", "false").lower() == "true"

# Setup logging
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(levelname)s - %(message)s",
    handlers=[
        logging.FileHandler("logs/webhook.log"),
        logging.StreamHandler(),
    ],
)
logger = logging.getLogger(__name__)


class GitHubWebhookHandler(BaseHTTPRequestHandler):
    """Handle incoming GitHub webhook requests."""

    def do_GET(self):
        """Handle GET requests - health check."""
        if self.path == "/health":
            self.send_response(200)
            self.send_header("Content-type", "application/json")
            self.end_headers()
            response = {
                "status": "healthy",
                "timestamp": datetime.now().isoformat(),
                "service": "ThrillWiki Webhook Listener",
            }
            self.wfile.write(json.dumps(response).encode())
        else:
            self.send_response(404)
            self.end_headers()

    def do_POST(self):
        """Handle POST requests - webhook events."""
        try:
            content_length = int(self.headers["Content-Length"])
            post_data = self.rfile.read(content_length)

            # Verify webhook signature if secret is configured
            if WEBHOOK_SECRET:
                if not self._verify_signature(post_data):
                    logger.warning("Invalid webhook signature")
                    self.send_response(401)
                    self.end_headers()
                    return

            # Parse webhook payload
            try:
                payload = json.loads(post_data.decode("utf-8"))
            except json.JSONDecodeError:
                logger.error("Invalid JSON payload")
                self.send_response(400)
                self.end_headers()
                return

            # Handle webhook event
            event_type = self.headers.get("X-GitHub-Event")
            if self._should_deploy(event_type, payload):
                logger.info(f"Triggering deployment for {event_type} event")
                threading.Thread(
                    target=self._trigger_deployment, args=(payload,)
                ).start()

                self.send_response(200)
                self.send_header("Content-type", "application/json")
                self.end_headers()
                response = {
                    "status": "deployment_triggered",
                    "event": event_type,
                }
                self.wfile.write(json.dumps(response).encode())
            else:
                logger.info(f"Ignoring {event_type} event - no deployment needed")
                self.send_response(200)
                self.send_header("Content-type", "application/json")
                self.end_headers()
                response = {"status": "ignored", "event": event_type}
                self.wfile.write(json.dumps(response).encode())

        except Exception as e:
            logger.error(f"Error handling webhook: {e}")
            self.send_response(500)
            self.end_headers()

    def _verify_signature(self, payload_body):
        """Verify GitHub webhook signature."""
        signature = self.headers.get("X-Hub-Signature-256")
        if not signature:
            return False

        expected_signature = (
            "sha256="
            + hmac.new(
                WEBHOOK_SECRET.encode(), payload_body, hashlib.sha256
            ).hexdigest()
        )

        return hmac.compare_digest(signature, expected_signature)

    def _should_deploy(self, event_type, payload):
        """Determine if we should trigger a deployment."""
        if event_type == "push":
            # Deploy on push to main branch
            ref = payload.get("ref", "")
            target_ref = f"refs/heads/{DEPLOY_BRANCH}"
            return ref == target_ref
        elif event_type == "release":
            # Deploy on new releases
            action = payload.get("action", "")
            return action == "published"

        return False

    def _trigger_deployment(self, payload):
        """Trigger deployment to Linux VM."""
        try:
            commit_sha = payload.get("after") or payload.get("head_commit", {}).get(
                "id", "unknown"
            )
            commit_message = payload.get("head_commit", {}).get("message", "No message")

            logger.info(
                f"Starting deployment of commit {commit_sha[:8]}: {commit_message}"
            )

            # Execute deployment script on VM
            deploy_script = f"""
#!/bin/bash
set -e

echo "=== ThrillWiki Deployment Started ==="
echo "Commit: {commit_sha[:8]}"
echo "Message: {commit_message}"
echo "Timestamp: $(date)"

cd {PROJECT_PATH}

# Pull latest changes
git fetch origin
git checkout {DEPLOY_BRANCH}
git pull origin {DEPLOY_BRANCH}

# Run deployment script
./scripts/vm-deploy.sh

echo "=== Deployment Completed Successfully ==="
"""

            # Execute deployment on VM via SSH
            ssh_command = [
                "ssh",
                "-i",
                VM_KEY_PATH,
                "-o",
                "StrictHostKeyChecking=no",
                "-o",
                "UserKnownHostsFile=/dev/null",
                f"{VM_USER}@{VM_HOST}",
                deploy_script,
            ]

            result = subprocess.run(
                ssh_command,
                capture_output=True,
                text=True,
                timeout=300,  # 5 minute timeout
            )

            if result.returncode == 0:
                logger.info(f"Deployment successful for commit {commit_sha[:8]}")
                self._send_status_notification("success", commit_sha, commit_message)
            else:
                logger.error(
                    f"Deployment failed for commit {commit_sha[:8]}: {result.stderr}"
                )
                self._send_status_notification(
                    "failure", commit_sha, commit_message, result.stderr
                )

        except subprocess.TimeoutExpired:
            logger.error("Deployment timed out")
            self._send_status_notification("timeout", commit_sha, commit_message)
        except Exception as e:
            logger.error(f"Deployment error: {e}")
            self._send_status_notification("error", commit_sha, commit_message, str(e))

    def _send_status_notification(
        self, status, commit_sha, commit_message, error_details=None
    ):
        """Send deployment status notification (optional)."""
        # This could be extended to send notifications to Slack, Discord, etc.
        status_msg = (
            f"Deployment {status} for commit {commit_sha[:8]}: {commit_message}"
        )
        if error_details:
            status_msg += f"\nError: {error_details}"

        logger.info(f"Status: {status_msg}")

    def log_message(self, format, *args):
        """Override to use our logger."""
        logger.info(f"{self.client_address[0]} - {format % args}")


def main():
    """Main function to start the webhook listener."""
    import argparse

    parser = argparse.ArgumentParser(description="ThrillWiki GitHub Webhook Listener")
    parser.add_argument(
        "--port", type=int, default=WEBHOOK_PORT, help="Port to listen on"
    )
    parser.add_argument(
        "--test",
        action="store_true",
        help="Test configuration without starting server",
    )

    args = parser.parse_args()

    # Create logs directory
    os.makedirs("logs", exist_ok=True)

    # Validate configuration
    if not WEBHOOK_SECRET:
        logger.warning(
            "WEBHOOK_SECRET not set - webhook signature verification disabled"
        )

    if not all([VM_HOST, VM_USER, PROJECT_PATH]):
        logger.error("Missing required VM configuration")
        if args.test:
            print("❌ Configuration validation failed")
            return
        sys.exit(1)

    logger.info(f"Webhook listener configuration:")
    logger.info(f"  Port: {args.port}")
    logger.info(f"  Target VM: {VM_USER}@{VM_HOST}")
    logger.info(f"  Project path: {PROJECT_PATH}")
    logger.info(f"  Deploy branch: {DEPLOY_BRANCH}")

    if args.test:
        print("✅ Configuration validation passed")
        print(f"Webhook would listen on port {args.port}")
        print(f"Target: {VM_USER}@{VM_HOST}")
        return

    logger.info(f"Starting webhook listener on port {args.port}")

    try:
        server = HTTPServer(("0.0.0.0", args.port), GitHubWebhookHandler)
        logger.info(
            f"Webhook listener started successfully on http://0.0.0.0:{args.port}"
        )
        logger.info("Health check available at: /health")
        server.serve_forever()
    except KeyboardInterrupt:
        logger.info("Webhook listener stopped by user")
    except Exception as e:
        logger.error(f"Failed to start webhook listener: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()
