"""Webhook endpoint for automation triggers.

External services call POST /api/v1/webhooks/{token} with a JSON body
containing {"action": "activate"} or {"action": "deactivate"} to control
automations that have a webhook condition.  No API-key auth is required —
the secret token itself authenticates the caller.
"""

import secrets
import time
from collections import defaultdict

from fastapi import APIRouter, Depends, HTTPException, Request
from pydantic import BaseModel, Field

from wled_controller.api.dependencies import get_automation_engine, get_automation_store
from wled_controller.core.automations.automation_engine import AutomationEngine
from wled_controller.storage.automation import WebhookCondition
from wled_controller.storage.automation_store import AutomationStore
from wled_controller.utils import get_logger

logger = get_logger(__name__)
router = APIRouter()

# ---------------------------------------------------------------------------
# Simple in-memory rate limiter: 30 requests per 60-second window per IP
# ---------------------------------------------------------------------------
_RATE_LIMIT = 30
_RATE_WINDOW = 60.0  # seconds
_rate_hits: dict[str, list[float]] = defaultdict(list)


def _check_rate_limit(client_ip: str) -> None:
    """Raise 429 if *client_ip* exceeded the webhook rate limit."""
    now = time.time()
    window_start = now - _RATE_WINDOW
    # Prune timestamps outside the window
    timestamps = _rate_hits[client_ip]
    _rate_hits[client_ip] = [t for t in timestamps if t > window_start]
    if len(_rate_hits[client_ip]) >= _RATE_LIMIT:
        raise HTTPException(
            status_code=429,
            detail="Rate limit exceeded. Max 30 webhook requests per minute.",
        )
    _rate_hits[client_ip].append(now)

    # Periodic cleanup: remove IPs with no recent hits to prevent unbounded growth
    if len(_rate_hits) > 100:
        stale = [ip for ip, ts in _rate_hits.items() if not ts or ts[-1] < window_start]
        for ip in stale:
            del _rate_hits[ip]


class WebhookPayload(BaseModel):
    action: str = Field(description="'activate' or 'deactivate'")


@router.post(
    "/api/v1/webhooks/{token}",
    tags=["Webhooks"],
)
async def handle_webhook(
    token: str,
    body: WebhookPayload,
    request: Request,
    store: AutomationStore = Depends(get_automation_store),
    engine: AutomationEngine = Depends(get_automation_engine),
):
    """Receive a webhook call and set the corresponding condition state."""
    _check_rate_limit(request.client.host if request.client else "unknown")

    if body.action not in ("activate", "deactivate"):
        raise HTTPException(status_code=400, detail="action must be 'activate' or 'deactivate'")

    # Find the automation that owns this token
    for automation in store.get_all_automations():
        for condition in automation.conditions:
            if isinstance(condition, WebhookCondition) and secrets.compare_digest(condition.token, token):
                active = body.action == "activate"
                await engine.set_webhook_state(token, active)
                logger.info(
                    "Webhook %s: automation '%s' (%s) → %s",
                    token[:8], automation.name, automation.id, body.action,
                )
                return {
                    "ok": True,
                    "automation_id": automation.id,
                    "automation_name": automation.name,
                    "action": body.action,
                }

    raise HTTPException(status_code=404, detail="Webhook token not found")
