"""
Management command to simulate GitHub webhook events for local development.

Since GitHub can't reach localhost, this command generates fake webhook events
and sends them to your local webhook endpoint. Useful for testing the GitHub
sync integration without setting up tunnels.

IMPORTANT: The repository must exist in the database (be "connected") for events
to be processed. Events for non-connected repos are silently ignored.

Two ways to connect a repository:

1. Via OAuth (production flow):
   - Click "Connect GitHub" in the Sources UI
   - Complete OAuth flow → select repositories

2. Via dev command (bypasses OAuth):
   python manage.py create_dev_github_repo --workspace <id> --repo owner/name

Usage Examples:

    # Simulate events for a specific repo
    python manage.py simulate_github_webhooks --repo owner/repo

    # If no --repo specified, uses first connected repo in database
    python manage.py simulate_github_webhooks

    # With options
    python manage.py simulate_github_webhooks --repo owner/repo --interval 5 --count 10

    # Specific event type
    python manage.py simulate_github_webhooks --repo owner/repo --event-type issues

Event Flow:
1. This command generates fake webhook payloads with repo_id from database
2. Sends to local webhook endpoint (http://localhost:8000/integrations/github/webhook/)
3. Webhook handler looks up GitHubRepository by repo_id
4. If found: creates Thread/Message records → appears in activity feed
5. If not found: event is ignored (check that repo is connected)
"""

import hashlib
import hmac
import json
import random
import time
import uuid
from datetime import UTC, datetime

import requests
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError


class Command(BaseCommand):
    help = "Simulate GitHub webhook events for local development testing"

    def add_arguments(self, parser):
        parser.add_argument(
            "--repo",
            type=str,
            help="Repository in owner/repo format (e.g., myorg/myrepo)",
        )
        parser.add_argument(
            "--interval",
            type=int,
            default=10,
            help="Seconds between events (default: 10)",
        )
        parser.add_argument(
            "--endpoint",
            type=str,
            default="http://localhost:8000/integrations/github/webhook/",
            help="Webhook endpoint URL",
        )
        parser.add_argument(
            "--count",
            type=int,
            default=0,
            help="Number of events to send (0 = infinite, default: 0)",
        )
        parser.add_argument(
            "--event-type",
            type=str,
            choices=["issues", "pull_request", "issue_comment", "discussion", "random"],
            default="random",
            help="Type of event to simulate (default: random)",
        )

    def handle(self, *args, **options):
        repo = options["repo"]
        interval = options["interval"]
        endpoint = options["endpoint"]
        count = options["count"]
        event_type = options["event_type"]

        webhook_secret = settings.GITHUB_WEBHOOK_SECRET
        if not webhook_secret:
            raise CommandError("GITHUB_WEBHOOK_SECRET not configured. Set it in your .env file.")

        # Parse repo
        if repo:
            if "/" not in repo:
                raise CommandError("--repo must be in owner/repo format")
            owner, name = repo.split("/", 1)
        else:
            # Look for any connected repository
            from integrations.models import GitHubRepository

            repo_obj = GitHubRepository.objects.filter(is_archived=False).first()
            if repo_obj:
                owner = repo_obj.owner
                name = repo_obj.name
                self.stdout.write(self.style.WARNING(f"No --repo specified, using {repo_obj.full_name}"))
            else:
                raise CommandError(
                    "No --repo specified and no connected repositories found. "
                    "Connect a repository first or specify --repo."
                )

        # Get repo_id from database if possible
        repo_id = self._get_repo_id(owner, name)

        self.stdout.write(
            self.style.SUCCESS(
                f"Starting webhook simulator:\n"
                f"  Repository: {owner}/{name}\n"
                f"  Endpoint:   {endpoint}\n"
                f"  Interval:   {interval}s\n"
                f"  Events:     {event_type}\n"
            )
        )
        self.stdout.write("Press Ctrl+C to stop\n")

        event_counter = 0
        try:
            while count == 0 or event_counter < count:
                # Generate event
                if event_type == "random":
                    selected_type = random.choice(["issues", "pull_request", "issue_comment"])
                else:
                    selected_type = event_type

                payload = self._generate_payload(selected_type, owner, name, repo_id)

                # Send webhook
                self._send_webhook(endpoint, selected_type, payload, webhook_secret)

                event_counter += 1
                self.stdout.write(
                    f"[{event_counter}] Sent {selected_type} event " f"(action: {payload.get('action', 'n/a')})"
                )

                if count == 0 or event_counter < count:
                    time.sleep(interval)

        except KeyboardInterrupt:
            self.stdout.write(self.style.WARNING("\nStopped by user"))

        self.stdout.write(self.style.SUCCESS(f"\nSent {event_counter} events total"))

    def _get_repo_id(self, owner: str, name: str) -> int:
        """Try to get repo_id from database, or generate a fake one."""
        from integrations.models import GitHubRepository

        try:
            repo = GitHubRepository.objects.get(owner=owner, name=name, is_archived=False)
            return repo.repo_id
        except GitHubRepository.DoesNotExist:
            # Generate a deterministic fake ID based on the name
            return abs(hash(f"{owner}/{name}")) % 1000000000

    def _generate_payload(self, event_type: str, owner: str, name: str, repo_id: int) -> dict:
        """Generate a realistic webhook payload for the given event type."""
        now = datetime.now(UTC).isoformat()

        # Base repository object used in all payloads
        repo_payload = {
            "id": repo_id,
            "node_id": f"R_{uuid.uuid4().hex[:16]}",
            "name": name,
            "full_name": f"{owner}/{name}",
            "private": False,
            "owner": {
                "login": owner,
                "id": abs(hash(owner)) % 100000000,
                "node_id": f"U_{uuid.uuid4().hex[:16]}",
                "type": "Organization" if "org" in owner.lower() else "User",
            },
            "html_url": f"https://github.com/{owner}/{name}",
        }

        # Base sender (who triggered the event)
        sender = {
            "login": f"dev-user-{random.randint(1, 100)}",
            "id": random.randint(1000000, 9999999),
            "node_id": f"U_{uuid.uuid4().hex[:16]}",
            "type": "User",
            "avatar_url": "https://avatars.githubusercontent.com/u/1?v=4",
        }

        if event_type == "issues":
            return self._generate_issue_event(repo_payload, sender, now)
        elif event_type == "pull_request":
            return self._generate_pr_event(repo_payload, sender, now)
        elif event_type == "issue_comment":
            return self._generate_issue_comment_event(repo_payload, sender, now)
        elif event_type == "discussion":
            return self._generate_discussion_event(repo_payload, sender, now)
        else:
            return self._generate_issue_event(repo_payload, sender, now)

    def _generate_issue_event(self, repo: dict, sender: dict, now: str) -> dict:
        """Generate an issues webhook payload."""
        actions = ["opened", "closed", "reopened", "labeled", "edited"]
        action = random.choice(actions)
        issue_number = random.randint(1, 500)

        titles = [
            "Bug: Application crashes on startup",
            "Feature request: Add dark mode",
            "Documentation: Update README",
            "Performance: Slow query optimization",
            "Security: Update dependencies",
            "Enhancement: Improve error messages",
            "Fix: Mobile responsive layout",
            "Chore: Refactor database module",
        ]

        bodies = [
            "This issue needs to be addressed soon.",
            "I've noticed this problem in production.",
            "Would be great to have this feature.",
            "This has been requested by multiple users.",
            "Detailed reproduction steps are in the attached file.",
        ]

        return {
            "action": action,
            "issue": {
                "id": random.randint(1000000000, 9999999999),
                "node_id": f"I_{uuid.uuid4().hex[:16]}",
                "number": issue_number,
                "title": random.choice(titles),
                "body": random.choice(bodies),
                "state": "closed" if action == "closed" else "open",
                "user": sender,
                "labels": [],
                "created_at": now,
                "updated_at": now,
                "html_url": f"{repo['html_url']}/issues/{issue_number}",
            },
            "repository": repo,
            "sender": sender,
        }

    def _generate_pr_event(self, repo: dict, sender: dict, now: str) -> dict:
        """Generate a pull_request webhook payload."""
        actions = ["opened", "closed", "reopened", "synchronize", "edited"]
        action = random.choice(actions)
        pr_number = random.randint(1, 300)

        titles = [
            "feat: Add user authentication",
            "fix: Resolve memory leak in cache",
            "docs: Update API documentation",
            "refactor: Clean up legacy code",
            "test: Add integration tests",
            "chore: Bump dependencies",
            "feat: Implement webhook handlers",
            "fix: Handle edge case in validation",
        ]

        return {
            "action": action,
            "number": pr_number,
            "pull_request": {
                "id": random.randint(1000000000, 9999999999),
                "node_id": f"PR_{uuid.uuid4().hex[:16]}",
                "number": pr_number,
                "title": random.choice(titles),
                "body": "This PR implements the requested changes.",
                "state": "closed" if action == "closed" else "open",
                "merged": action == "closed" and random.choice([True, False]),
                "user": sender,
                "draft": False,
                "head": {
                    "ref": f"feature-{random.randint(1, 100)}",
                    "sha": uuid.uuid4().hex[:40],
                },
                "base": {
                    "ref": "main",
                    "sha": uuid.uuid4().hex[:40],
                },
                "created_at": now,
                "updated_at": now,
                "html_url": f"{repo['html_url']}/pull/{pr_number}",
                "additions": random.randint(10, 500),
                "deletions": random.randint(5, 200),
                "changed_files": random.randint(1, 20),
            },
            "repository": repo,
            "sender": sender,
        }

    def _generate_issue_comment_event(self, repo: dict, sender: dict, now: str) -> dict:
        """Generate an issue_comment webhook payload."""
        actions = ["created", "edited"]
        action = random.choice(actions)
        issue_number = random.randint(1, 500)

        comments = [
            "Thanks for reporting this! I'll look into it.",
            "Can you provide more details?",
            "I've reproduced this issue. Working on a fix.",
            "This should be fixed in the latest release.",
            "LGTM! Great work on this.",
            "+1 on this feature request.",
            "I'm seeing the same behavior.",
            "Have you tried clearing the cache?",
        ]

        return {
            "action": action,
            "issue": {
                "id": random.randint(1000000000, 9999999999),
                "node_id": f"I_{uuid.uuid4().hex[:16]}",
                "number": issue_number,
                "title": "Some existing issue",
                "state": "open",
                "user": sender,
                "html_url": f"{repo['html_url']}/issues/{issue_number}",
            },
            "comment": {
                "id": random.randint(1000000000, 9999999999),
                "node_id": f"IC_{uuid.uuid4().hex[:16]}",
                "body": random.choice(comments),
                "user": sender,
                "created_at": now,
                "updated_at": now,
                "html_url": f"{repo['html_url']}/issues/{issue_number}#issuecomment-{random.randint(1000000, 9999999)}",
            },
            "repository": repo,
            "sender": sender,
        }

    def _generate_discussion_event(self, repo: dict, sender: dict, now: str) -> dict:
        """Generate a discussion webhook payload."""
        actions = ["created", "answered", "edited"]
        action = random.choice(actions)
        discussion_number = random.randint(1, 200)

        titles = [
            "How do I configure the API?",
            "Best practices for deployment",
            "Feature discussion: New UI",
            "Community feedback wanted",
            "Q&A: Getting started guide",
        ]

        return {
            "action": action,
            "discussion": {
                "id": random.randint(1000000000, 9999999999),
                "node_id": f"D_{uuid.uuid4().hex[:16]}",
                "number": discussion_number,
                "title": random.choice(titles),
                "body": "Looking for input from the community on this topic.",
                "state": "open",
                "user": sender,
                "category": {
                    "id": random.randint(1000, 9999),
                    "name": random.choice(["General", "Q&A", "Ideas", "Show and tell"]),
                },
                "created_at": now,
                "updated_at": now,
                "html_url": f"{repo['html_url']}/discussions/{discussion_number}",
            },
            "repository": repo,
            "sender": sender,
        }

    def _send_webhook(self, endpoint: str, event_type: str, payload: dict, secret: str) -> None:
        """Send a signed webhook request to the endpoint."""
        body = json.dumps(payload).encode("utf-8")

        # Compute HMAC signature
        signature = (
            "sha256="
            + hmac.new(
                secret.encode("utf-8"),
                body,
                hashlib.sha256,
            ).hexdigest()
        )

        headers = {
            "Content-Type": "application/json",
            "X-GitHub-Event": event_type,
            "X-GitHub-Delivery": str(uuid.uuid4()),
            "X-Hub-Signature-256": signature,
        }

        try:
            response = requests.post(endpoint, data=body, headers=headers, timeout=10)
            if response.status_code != 200:
                self.stdout.write(self.style.WARNING(f"  Warning: Got {response.status_code} - {response.text[:100]}"))
        except requests.RequestException as e:
            self.stdout.write(self.style.ERROR(f"  Error: {e}"))
