"""CLI commands for managing webhook endpoints."""

from __future__ import annotations

import json
from pathlib import Path

import click

from oclawma.cli_ui import (
    accent,
    header,
    highlight,
    key_value,
    muted,
    print_error,
    print_info,
    print_success,
    print_warning,
    subheader,
)
from oclawma.config import get_config_dir
from oclawma.queue import JobPriority
from oclawma.webhooks import WebhookEndpoint

DEFAULT_WEBHOOKS_FILE = get_config_dir() / "webhooks.json"


def load_webhooks_config(path: Path | None = None) -> dict:
    """Load webhooks configuration from file."""
    config_path = path or DEFAULT_WEBHOOKS_FILE
    if config_path.exists():
        with open(config_path) as f:
            return json.load(f)
    return {"endpoints": []}


def save_webhooks_config(config: dict, path: Path | None = None) -> None:
    """Save webhooks configuration to file."""
    config_path = path or DEFAULT_WEBHOOKS_FILE
    config_path.parent.mkdir(parents=True, exist_ok=True)
    with open(config_path, "w") as f:
        json.dump(config, f, indent=2)


@click.group(name="webhooks")
@click.option(
    "--config-path",
    type=click.Path(),
    default=str(DEFAULT_WEBHOOKS_FILE),
    help="Path to webhooks configuration file",
    show_default=True,
)
@click.pass_context
def webhooks_cli(ctx: click.Context, config_path: str) -> None:
    """Manage webhook endpoints.

    Configure and manage incoming webhook endpoints that can receive
    HTTP requests and enqueue jobs.

    \b
    Examples:
        oclawma webhooks list                    # List all endpoints
        oclawma webhooks add github --secret xxx # Add GitHub webhook
        oclawma webhooks remove github           # Remove an endpoint
        oclawma webhooks enable github           # Enable an endpoint
        oclawma webhooks disable github          # Disable an endpoint
    """
    ctx.ensure_object(dict)
    ctx.obj["config_path"] = Path(config_path)


@webhooks_cli.command(name="list")
@click.option(
    "--verbose",
    "-v",
    is_flag=True,
    help="Show detailed endpoint information",
)
@click.pass_context
def webhooks_list(ctx: click.Context, verbose: bool) -> None:
    """List all configured webhook endpoints."""
    config_path = ctx.obj["config_path"]
    config = load_webhooks_config(config_path)

    if not config.get("endpoints"):
        print_info("No webhook endpoints configured")
        return

    click.echo(header("WEBHOOK ENDPOINTS", width=58))
    click.echo()

    for ep_data in config["endpoints"]:
        status_icon = "✓" if ep_data.get("enabled", True) else "✗"
        status_color = "green" if ep_data.get("enabled", True) else "red"

        click.echo(f"{accent('/' + ep_data['path'], bold=True)}")
        click.echo(key_value("  Status", click.style(status_icon, fg=status_color)))
        click.echo(key_value("  Provider", ep_data.get("provider", "generic")))
        click.echo(key_value("  Job Type", ep_data.get("job_type", "webhook")))
        click.echo(key_value("  Priority", ep_data.get("priority", 0)))

        if verbose:
            # Mask the secret for security
            secret = ep_data.get("secret", "")
            masked_secret = secret[:4] + "..." + secret[-4:] if len(secret) > 8 else "****"
            click.echo(key_value("  Secret", masked_secret))

            if ep_data.get("metadata"):
                click.echo(key_value("  Metadata", json.dumps(ep_data["metadata"])))

        click.echo()

    click.echo(muted(f"Total: {len(config['endpoints'])} endpoints"))


@webhooks_cli.command(name="add")
@click.argument("path")
@click.option(
    "--secret",
    required=True,
    help="Secret key for signature verification",
    prompt=True,
    hide_input=True,
)
@click.option(
    "--provider",
    type=click.Choice(["github", "stripe", "generic"]),
    default="generic",
    help="Webhook provider type",
    show_default=True,
)
@click.option(
    "--job-type",
    default="webhook",
    help="Job type for enqueued jobs",
    show_default=True,
)
@click.option(
    "--priority",
    type=click.Choice(["CRITICAL", "HIGH", "NORMAL", "LOW"]),
    default="NORMAL",
    help="Job priority",
    show_default=True,
)
@click.option(
    "--metadata",
    help="Additional metadata as JSON string",
)
@click.pass_context
def webhooks_add(
    ctx: click.Context,
    path: str,
    secret: str,
    provider: str,
    job_type: str,
    priority: str,
    metadata: str | None,
) -> None:
    """Add a new webhook endpoint.

    PATH is the URL path for the webhook (e.g., 'github', 'stripe/webhooks').
    """
    config_path = ctx.obj["config_path"]
    config = load_webhooks_config(config_path)

    # Check for existing endpoint
    for ep in config["endpoints"]:
        if ep["path"] == path:
            print_error(f"Endpoint '{path}' already exists")
            raise click.Abort()

    # Parse metadata if provided
    meta_dict = {}
    if metadata:
        try:
            meta_dict = json.loads(metadata)
        except json.JSONDecodeError as e:
            print_error(f"Invalid metadata JSON: {e}")
            raise click.Abort() from e

    # Map priority string to value
    priority_map = {
        "CRITICAL": JobPriority.CRITICAL.value,
        "HIGH": JobPriority.HIGH.value,
        "NORMAL": JobPriority.NORMAL.value,
        "LOW": JobPriority.LOW.value,
    }

    endpoint = {
        "path": path,
        "secret": secret,
        "provider": provider,
        "job_type": job_type,
        "priority": priority_map[priority],
        "enabled": True,
        "metadata": meta_dict,
    }

    # Validate the endpoint
    try:
        WebhookEndpoint.from_dict(endpoint)
    except ValueError as e:
        print_error(f"Invalid endpoint configuration: {e}")
        raise click.Abort() from e

    config["endpoints"].append(endpoint)
    save_webhooks_config(config, config_path)

    print_success(f"Added webhook endpoint: {path}")
    click.echo(f"  Provider: {highlight(provider)}")
    click.echo(f"  Job Type: {job_type}")
    click.echo(f"  Priority: {priority}")
    click.echo(f"  Webhook URL: {muted(f'/webhooks/{path}')}")


@webhooks_cli.command(name="remove")
@click.argument("path")
@click.confirmation_option(
    prompt="Are you sure you want to remove this webhook endpoint?",
    help="Confirm removal",
)
@click.pass_context
def webhooks_remove(ctx: click.Context, path: str) -> None:
    """Remove a webhook endpoint."""
    config_path = ctx.obj["config_path"]
    config = load_webhooks_config(config_path)

    # Find and remove endpoint
    for i, ep in enumerate(config["endpoints"]):
        if ep["path"] == path:
            del config["endpoints"][i]
            save_webhooks_config(config, config_path)
            print_success(f"Removed webhook endpoint: {path}")
            return

    print_error(f"Endpoint '{path}' not found")
    raise click.Abort()


@webhooks_cli.command(name="enable")
@click.argument("path")
@click.pass_context
def webhooks_enable(ctx: click.Context, path: str) -> None:
    """Enable a webhook endpoint."""
    config_path = ctx.obj["config_path"]
    config = load_webhooks_config(config_path)

    for ep in config["endpoints"]:
        if ep["path"] == path:
            ep["enabled"] = True
            save_webhooks_config(config, config_path)
            print_success(f"Enabled webhook endpoint: {path}")
            return

    print_error(f"Endpoint '{path}' not found")
    raise click.Abort()


@webhooks_cli.command(name="disable")
@click.argument("path")
@click.pass_context
def webhooks_disable(ctx: click.Context, path: str) -> None:
    """Disable a webhook endpoint."""
    config_path = ctx.obj["config_path"]
    config = load_webhooks_config(config_path)

    for ep in config["endpoints"]:
        if ep["path"] == path:
            ep["enabled"] = False
            save_webhooks_config(config, config_path)
            print_success(f"Disabled webhook endpoint: {path}")
            return

    print_error(f"Endpoint '{path}' not found")
    raise click.Abort()


@webhooks_cli.command(name="show")
@click.argument("path")
@click.pass_context
def webhooks_show(ctx: click.Context, path: str) -> None:
    """Show detailed information about a webhook endpoint."""
    config_path = ctx.obj["config_path"]
    config = load_webhooks_config(config_path)

    for ep in config["endpoints"]:
        if ep["path"] == path:
            click.echo(header(f"WEBHOOK: /{path}", width=58))
            click.echo()

            status = "enabled" if ep.get("enabled", True) else "disabled"
            status_color = "green" if ep.get("enabled", True) else "red"

            click.echo(key_value("Path", f"/{ep['path']}"))
            click.echo(key_value("Status", click.style(status, fg=status_color)))
            click.echo(key_value("Provider", ep.get("provider", "generic")))
            click.echo(key_value("Job Type", ep.get("job_type", "webhook")))
            click.echo(key_value("Priority", ep.get("priority", 0)))

            # Mask the secret
            secret = ep.get("secret", "")
            masked_secret = secret[:4] + "..." + secret[-4:] if len(secret) > 8 else "****"
            click.echo(key_value("Secret", masked_secret))

            if ep.get("metadata"):
                click.echo(key_value("Metadata", ""))
                for key, value in ep["metadata"].items():
                    click.echo(f"  {key}: {highlight(str(value))}")

            click.echo()
            click.echo(subheader("USAGE"))
            click.echo(f"  Webhook URL: {muted(f'/webhooks/{path}')}")

            if ep.get("provider") == "github":
                click.echo(f"  Header: {muted('X-Hub-Signature-256: sha256=<signature>')}")
            elif ep.get("provider") == "stripe":
                click.echo(f"  Header: {muted('Stripe-Signature: t=<timestamp>,v1=<signature>')}")
            else:
                click.echo(f"  Header: {muted('X-Webhook-Signature: <signature>')}")

            return

    print_error(f"Endpoint '{path}' not found")
    raise click.Abort()


@webhooks_cli.command(name="test")
@click.argument("path")
@click.option(
    "--payload",
    default='{"test": true}',
    help="JSON payload to send",
)
@click.option(
    "--secret",
    help="Override the secret (uses stored secret if not provided)",
)
@click.option(
    "--base-url",
    default="http://localhost:8000",
    help="Base URL for the webhook server",
    show_default=True,
)
@click.pass_context
def webhooks_test(
    ctx: click.Context,
    path: str,
    payload: str,
    secret: str | None,
    base_url: str,
) -> None:
    """Test a webhook endpoint by sending a test request.

    This generates a valid signature and sends a test payload to the webhook.
    """
    import hashlib
    import hmac

    import requests

    config_path = ctx.obj["config_path"]
    config = load_webhooks_config(config_path)

    # Find endpoint
    endpoint = None
    for ep in config["endpoints"]:
        if ep["path"] == path:
            endpoint = ep
            break

    if not endpoint:
        print_error(f"Endpoint '{path}' not found")
        raise click.Abort()

    use_secret = secret or endpoint.get("secret")
    if not use_secret:
        print_error("No secret configured for endpoint")
        raise click.Abort()

    # Parse and validate payload
    try:
        payload_dict = json.loads(payload)
    except json.JSONDecodeError as e:
        print_error(f"Invalid JSON payload: {e}")
        raise click.Abort() from e

    payload_bytes = json.dumps(payload_dict).encode()

    # Generate signature based on provider
    provider = endpoint.get("provider", "generic")
    headers = {"Content-Type": "application/json"}

    if provider == "github":
        signature = hmac.new(
            use_secret.encode(),
            payload_bytes,
            hashlib.sha256,
        ).hexdigest()
        headers["X-Hub-Signature-256"] = f"sha256={signature}"
    elif provider == "stripe":
        import time

        timestamp = str(int(time.time()))
        signed_payload = f"{timestamp}.{payload_bytes.decode()}"
        signature = hmac.new(
            use_secret.encode(),
            signed_payload.encode(),
            hashlib.sha256,
        ).hexdigest()
        headers["Stripe-Signature"] = f"t={timestamp},v1={signature}"
    else:
        signature = hmac.new(
            use_secret.encode(),
            payload_bytes,
            hashlib.sha256,
        ).hexdigest()
        headers["X-Webhook-Signature"] = signature

    # Send request
    url = f"{base_url}/webhooks/{path}"
    click.echo(f"Sending test request to {url}")

    try:
        response = requests.post(url, data=payload_bytes, headers=headers, timeout=10)

        click.echo()
        if response.status_code == 202:
            print_success(f"Webhook accepted (HTTP {response.status_code})")
        else:
            print_warning(f"Unexpected status code: {response.status_code}")

        click.echo(subheader("Response"))
        try:
            click.echo(json.dumps(response.json(), indent=2))
        except json.JSONDecodeError:
            click.echo(response.text)

    except requests.RequestException as e:
        print_error(f"Request failed: {e}")
        raise click.Abort() from e
