from __future__ import annotations

from typing import Any, Mapping

from .models import WebhookEnvelope


class EasybillWebhookParser:
    def __init__(self, secret: str | None = None) -> None:
        self.secret = secret

    def parse(
        self,
        *,
        headers: Mapping[str, Any],
        payload: Mapping[str, Any] | None,
        content_type: str | None = None,
    ) -> WebhookEnvelope:
        normalized_content_type = (content_type or "application/json").lower()

        if "json" in normalized_content_type:
            return self.parse_json(headers=headers, payload=payload)

        if "form" in normalized_content_type or "x-www-form-urlencoded" in normalized_content_type or "multipart/form-data" in normalized_content_type:
            return self.parse_form(headers=headers, form_data=payload or {})

        if isinstance(payload, Mapping):
            return self.parse_json(headers=headers, payload=payload)

        raise ValueError(f"Unsupported webhook content type: {content_type}")

    def parse_json(self, *, headers: Mapping[str, Any], payload: Mapping[str, Any] | None) -> WebhookEnvelope:
        normalized_payload = dict(payload or {})
        return self._build_envelope(headers, normalized_payload)

    def parse_form(self, *, headers: Mapping[str, Any], form_data: Mapping[str, Any]) -> WebhookEnvelope:
        normalized_payload = {
            str(key): self._coerce_scalar(value)
            for key, value in form_data.items()
        }
        return self._build_envelope(headers, normalized_payload)

    @staticmethod
    def build_acknowledgement() -> dict[str, str]:
        return {"status": "ok"}

    def _build_envelope(self, headers: Mapping[str, Any], payload: dict[str, Any]) -> WebhookEnvelope:
        normalized_headers = {str(key).lower(): str(value) for key, value in headers.items()}
        event = self._get_required_header(normalized_headers, "x-easybill-event")
        delivery_id = self._get_required_header(normalized_headers, "x-easybill-webhook-delivery")

        return WebhookEnvelope(
            event=event,
            delivery_id=delivery_id,
            payload=payload,
            resource_id=self._extract_resource_id(payload),
            headers=normalized_headers,
        )

    @staticmethod
    def _get_required_header(headers: Mapping[str, str], name: str) -> str:
        value = headers.get(name)
        if not value:
            raise ValueError(f"Missing required header: {name}")
        return value

    @staticmethod
    def _extract_resource_id(payload: Mapping[str, Any]) -> int | None:
        for key in ("id", "document_id", "customer_id", "position_id", "project_id", "task_id"):
            value = payload.get(key)
            if value is None:
                continue
            try:
                return int(value)
            except (TypeError, ValueError):
                continue
        return None

    @staticmethod
    def _coerce_scalar(value: Any) -> Any:
        if isinstance(value, list):
            return [EasybillWebhookParser._coerce_scalar(item) for item in value]
        if isinstance(value, str):
            stripped = value.strip()
            if stripped.isdigit():
                return int(stripped)
            lowered = stripped.lower()
            if lowered in {"true", "false"}:
                return lowered == "true"
            return stripped
        return value
