from __future__ import annotations

import hashlib
import hmac
import json
import os
import time
from typing import Any


class StripeWebhookVerifier:
    def __init__(
        self,
        *,
        webhook_secret: str,
        tolerance_seconds: int = 300,
        now_epoch: int | None = None,
    ) -> None:
        if not str(webhook_secret or "").strip():
            raise ValueError("STRIPE_WEBHOOK_SECRET is required")
        if tolerance_seconds <= 0:
            raise ValueError("tolerance_seconds must be greater than zero")
        self._webhook_secret = webhook_secret.strip()
        self.tolerance_seconds = tolerance_seconds
        self._now_epoch = now_epoch

    @classmethod
    def from_env(cls) -> "StripeWebhookVerifier":
        return cls(webhook_secret=os.environ.get("STRIPE_WEBHOOK_SECRET", ""))

    def verify_event(self, *, raw_body: str | bytes, stripe_signature: str) -> dict[str, Any]:
        body = raw_body.encode("utf-8") if isinstance(raw_body, str) else bytes(raw_body)
        timestamp, signatures = _parse_signature_header(stripe_signature)
        now = self._now_epoch if self._now_epoch is not None else int(time.time())
        if abs(now - timestamp) > self.tolerance_seconds:
            raise ValueError("stripe webhook signature timestamp is outside tolerance")
        signed_payload = f"{timestamp}.".encode("utf-8") + body
        expected = hmac.new(self._webhook_secret.encode("utf-8"), signed_payload, hashlib.sha256).hexdigest()
        if not any(hmac.compare_digest(expected, signature) for signature in signatures):
            raise ValueError("stripe webhook signature verification failed")
        try:
            event = json.loads(body.decode("utf-8"))
        except json.JSONDecodeError as exc:
            raise ValueError("stripe webhook payload must be valid JSON") from exc
        if not isinstance(event, dict) or not event.get("id") or not event.get("type"):
            raise ValueError("stripe webhook event is invalid")
        return event


def _parse_signature_header(stripe_signature: str) -> tuple[int, list[str]]:
    values: dict[str, list[str]] = {}
    for item in str(stripe_signature or "").split(","):
        key, separator, value = item.partition("=")
        if not separator:
            continue
        values.setdefault(key, []).append(value)
    try:
        timestamp = int(values.get("t", [""])[0])
    except ValueError as exc:
        raise ValueError("stripe webhook signature timestamp is invalid") from exc
    signatures = [item for item in values.get("v1", []) if item]
    if not signatures:
        raise ValueError("stripe webhook signature is missing")
    return timestamp, signatures
