import time
import traceback
from typing import List, Optional
import httpx
from .types import TrackedRequest, RemoteStorageOptions


class RemoteStorage:
    def __init__(self, options: RemoteStorageOptions):
        if not options.get("api_key"):
            raise ValueError("remoteStorage.api_key is required for remote storage")

        self.endpoint = options.get("endpoint", "https://api.tokentracker.dev/api/track")
        self.api_key = options["api_key"]
        self.project_name = options.get("project_name")
        self.timeout = options.get("timeout", 10000) / 1000
        self.retries = options.get("retries", 3)
        self.retry_delay = options.get("retry_delay", 1000) / 1000

    def _serialize_error(self, error: Exception) -> dict:
        serialized = {
            "name": type(error).__name__,
            "message": str(error),
            "stack": "".join(traceback.format_exception(type(error), error, error.__traceback__)),
        }

        for key in dir(error):
            if not key.startswith("_"):
                try:
                    value = getattr(error, key)
                    if not callable(value):
                        serialized[key] = value
                except:
                    pass

        error_msg = str(error)
        if error_msg.strip().startswith(("{", "[")):
            try:
                import json
                serialized["parsedMessage"] = json.loads(error_msg)
            except:
                pass

        return serialized

    def _fetch_with_retry(
        self, url: str, method: str, headers: dict, json_data: Optional[dict] = None, attempt: int = 0
    ) -> httpx.Response:
        try:
            with httpx.Client(timeout=self.timeout, follow_redirects=True) as client:
                if method == "POST":
                    response = client.post(url, headers=headers, json=json_data)
                else:
                    response = client.get(url, headers=headers)

                if not response.is_success and response.status_code >= 500 and attempt < self.retries:
                    time.sleep(self.retry_delay * (2 ** attempt))
                    return self._fetch_with_retry(url, method, headers, json_data, attempt + 1)

                return response
        except (httpx.TimeoutException, httpx.ConnectError) as error:
            if attempt < self.retries:
                time.sleep(self.retry_delay * (2 ** attempt))
                return self._fetch_with_retry(url, method, headers, json_data, attempt + 1)
            raise error

    def add(self, request: TrackedRequest) -> None:
        request_with_project = {
            **request,
            "projectName": self.project_name,
            "responseTime": request.get("response_time"),
            "searchGrounding": request.get("search_grounding", False),
            "receivedSearchGroundedResponse": request.get("received_search_grounded_response", False),
            "error": self._serialize_error(request["error"]) if request.get("error") else None,
            "metadata": request.get("metadata"),
        }

        if "timestamp" in request_with_project and hasattr(request_with_project["timestamp"], "isoformat"):
            request_with_project["timestamp"] = request_with_project["timestamp"].isoformat()

        headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {self.api_key}",
        }

        response = self._fetch_with_retry(
            self.endpoint,
            "POST",
            headers,
            request_with_project
        )

        if not response.is_success:
            raise Exception(f"Remote storage failed: {response.status_code} {response.reason_phrase}")

    def get_all(self) -> List[TrackedRequest]:
        headers = {
            "Authorization": f"Bearer {self.api_key}",
        }

        response = self._fetch_with_retry(self.endpoint, "GET", headers)

        if not response.is_success:
            raise Exception(f"Failed to fetch tracked requests: {response.status_code}")

        return response.json()

    def clear(self) -> None:
        raise Exception("Clear not supported for remote storage")
