import os
import json
from typing import Any, Dict, Optional

import httpx
import structlog
from pydantic import BaseModel


logger = structlog.get_logger("ai_analytics.service")


class AiAnalyticsConfig(BaseModel):
    enabled: bool = True
    url: str = "https://api.zavedenia.com/ai/AiUsageLogs.php"
    local_url: str = "http://localhost:8000/ai-analytics/log"
    api_key: str
    auth_token: Optional[str] = None
    timeout: float = 10.0
    provider: str = "openai"

    @classmethod
    def from_env(cls) -> "AiAnalyticsConfig":
        api_key = os.getenv("AI_ANALYTICS_API_KEY")
        if not api_key:
            raise ValueError("Environment variable AI_ANALYTICS_API_KEY not set")

        return cls(
            enabled=os.getenv("AI_ANALYTICS_ENABLED", "true").lower() != "false",
            url=os.getenv("AI_ANALYTICS_URL", "https://api.zavedenia.com/ai/AiUsageLogs.php"),
            local_url=os.getenv("AI_ANALYTICS_LOCAL_URL", "http://localhost:8000/ai-analytics/log"),
            api_key=api_key,
            auth_token=os.getenv("AI_ANALYTICS_SERVICE_KEY") or os.getenv("AUTHORIZATION_KEY"),
            timeout=float(os.getenv("AI_ANALYTICS_TIMEOUT", "10.0")),
            provider=os.getenv("AI_ANALYTICS_PROVIDER", "openai"),
        )


class AiAnalyticsService:
    def __init__(self, config: AiAnalyticsConfig):
        self.config = config
        self._client: Optional[httpx.AsyncClient] = None

    @property
    def enabled(self) -> bool:
        return self.config.enabled

    def _get_client(self) -> httpx.AsyncClient:
        if self._client is None:
            self._client = httpx.AsyncClient(timeout=self.config.timeout)
        return self._client

    async def submit_log_request(
        self,
        *,
        city: str,
        zavedenia_id: int,
        service: str,
        model: str,
        tokens_input: Dict[str, Any],
        tokens_output: Dict[str, Any],
        data_input: Dict[str, Any],
        data_output: Dict[str, Any],
        execution_time: int,
    ) -> None:
        if not self.enabled:
            return

        payload: Dict[str, Any] = {
            "city": city,
            "zavedenia_id": zavedenia_id,
            "service": service,
            "model": model,
            "tokens_input": tokens_input,
            "tokens_output": tokens_output,
            "data_input": data_input,
            "data_output": data_output,
            "execution_time": execution_time,
        }

        headers = {"Content-Type": "application/json"}
        if self.config.auth_token:
            headers["Authorization"] = f"Bearer {self.config.auth_token}"

        logger.info(
            "ai_analytics_local_request",
            url=self.config.local_url,
            payload=payload,
        )
        logger.info(
            "ai_analytics_local_curl",
            curl_command=(
                "curl --location "
                f"'{self.config.local_url}' "
                f"--header 'Authorization: Bearer {'***' if self.config.auth_token else ''}' "
                "--header 'Content-Type: application/json' "
                f"--data '{json.dumps(payload, ensure_ascii=False)}'"
            ),
        )

        response = await self._get_client().post(
            self.config.local_url,
            json=payload,
            headers=headers,
        )
        if response.is_error:
            logger.warning(
                "ai_analytics_local_http_error",
                status_code=response.status_code,
                response_text=response.text,
                payload=payload,
            )
            response.raise_for_status()

        logger.info(
            "ai_analytics_local_logged",
            status_code=response.status_code,
            city=city,
            zavedenia_id=zavedenia_id,
        )

    async def log_usage(
        self,
        *,
        city: Optional[str],
        zavedenia_id: Optional[int],
        service: Optional[str],
        model: str,
        tokens_input: Dict[str, Any],
        tokens_output: Dict[str, Any],
        data_input: Dict[str, Any],
        data_output: Dict[str, Any],
        execution_time: int,
    ) -> None:
        if not self.enabled:
            return

        normalized_city = self._normalize_city(city)
        normalized_zavedenia_id = self._normalize_zavedenia_id(zavedenia_id)

        payload: Dict[str, Any] = {
            "service": service or self.config.provider,
            "model": model,
            "tokens_input": tokens_input,
            "tokens_output": tokens_output,
            "data_input": data_input,
            "data_output": data_output,
            "execution_time": execution_time,
            "apiKey": self.config.api_key,
        }

        if normalized_city is not None:
            payload["city"] = normalized_city
        if normalized_zavedenia_id is not None:
            payload["zavedenia_id"] = normalized_zavedenia_id

        logger.info(
            "ai_analytics_request",
            url=self.config.url,
            payload={**payload, "apiKey": "***"},
        )
        logger.info(
            "ai_analytics_curl",
            curl_command=(
                "curl --location "
                f"'{self.config.url}' "
                "--header 'Content-Type: application/json' "
                f"--data-raw '{json.dumps({**payload, 'apiKey': '***'}, ensure_ascii=False)}'"
            ),
        )

        response = await self._get_client().post(
            self.config.url,
            json=payload,
            headers={"Content-Type": "application/json"},
        )
        if response.is_error:
            logger.warning(
                "ai_analytics_http_error",
                status_code=response.status_code,
                response_text=response.text,
                payload={**payload, "apiKey": "***"},
            )
            response.raise_for_status()

        logger.info(
            "ai_analytics_logged",
            model=model,
            city=normalized_city,
            zavedenia_id=normalized_zavedenia_id,
            status_code=response.status_code,
        )

    async def close(self) -> None:
        if self._client is not None:
            await self._client.aclose()
            self._client = None

    @staticmethod
    def _normalize_city(city: Optional[str]) -> Optional[str]:
        if city is None:
            return None
        normalized = city.strip()
        if not normalized or normalized == "City not set":
            return None
        return normalized

    @staticmethod
    def _normalize_zavedenia_id(zavedenia_id: Optional[int]) -> Optional[int]:
        if zavedenia_id is None or zavedenia_id < 0:
            return None
        return zavedenia_id
