from __future__ import annotations

import asyncio
from collections import defaultdict
from dataclasses import dataclass, field
from typing import Any

from camoufox.async_api import AsyncCamoufox
from human_requests import (
    ApiParent,
    HumanBrowser,
    HumanContext,
    HumanPage,
    api_child_field,
)
from human_requests.abstraction import FetchResponse, HttpMethod, Proxy
from human_requests.network_analyzer.anomaly_sniffer import HeaderAnomalySniffer

from .endpoints.advertising import ClassAdvertising
from .endpoints.catalog import ClassCatalog
from .endpoints.general import ClassGeneral
from .endpoints.geolocation import ClassGeolocation


@dataclass
class ChizhikAPI(ApiParent):
    """
    Клиент Чижика.
    """

    timeout_ms: float = 10000.0
    """Время ожидания ответа от сервера в миллисекундах."""
    headless: bool = True
    """Запускать браузер в headless режиме?"""
    test_mode: bool = False
    """Режим тестирования предполагает более глубокий _warmup который не требуется для обычного использования"""
    proxy: str | dict | Proxy | None = field(default_factory=Proxy.from_env)
    """Прокси-сервер для всех запросов (если нужен). По умолчанию берет из окружения (если есть).
    Принимает как формат Playwright, так и строчный формат."""
    browser_opts: dict[str, Any] = field(default_factory=dict)
    """Дополнительные опции для браузера (см. https://camoufox.com/python/installation/)"""
    API_URL: str = "https://app.chizhik.club/api"
    DELIVERY_API_URL: str = "https://app.chizhik.club/delivery/api"
    """URL для работы с каталогом."""
    MAIN_SITE_URL: str = "https://chizhik.club/catalog/"
    """URL главной страницы сайта."""
    MAIN_SITE_ORIGIN: str = "https://chizhik.club/"

    # будет создана в __post_init__
    session: HumanBrowser = field(init=False, repr=False)
    """Внутренняя сессия браузера для выполнения HTTP-запросов."""
    # будет создано в warmup
    ctx: HumanContext = field(init=False, repr=False)
    """Внутренний контекст сессии браузера"""
    page: HumanPage = field(init=False, repr=False)
    """Внутренний страница сессии браузера"""

    unstandard_headers: dict[str, str] = field(init=False, repr=False)
    """Список нестандартных заголовков пойманных при инициализации"""
    unstandard_urls: dict[str, list[str]] = field(init=False, repr=False)
    """Список нестандартных заголовков пойманных при инициализации"""

    Geolocation: ClassGeolocation = api_child_field(ClassGeolocation)
    """API для работы с геолокацией."""
    Catalog: ClassCatalog = api_child_field(ClassCatalog)
    """API для работы с каталогом товаров."""
    Advertising: ClassAdvertising = api_child_field(ClassAdvertising)
    """API для работы с рекламой."""
    General: ClassGeneral = api_child_field(ClassGeneral)
    """API для работы с общими функциями."""

    async def __aenter__(self):
        """Вход в контекстный менеджер с автоматическим прогревом сессии."""
        await self._warmup()
        return self

    # Прогрев сессии (headless ➜ cookie `session` ➜ accessToken)
    async def _warmup(self) -> None:
        """Прогрев сессии через браузер для получения человекоподобности."""
        px = self.proxy if isinstance(self.proxy, Proxy) else Proxy(self.proxy)
        br = await AsyncCamoufox(
            headless=self.headless,
            proxy=px.as_dict(),
            **self.browser_opts,
            block_images=True,
            i_know_what_im_doing=True,
        ).start()

        self.session = HumanBrowser.replace(br)
        self.ctx = await self.session.new_context()
        self.page = await self.ctx.new_page()
        self.page.on_error_screenshot_path = "screenshot.png"

        if self.test_mode:
            sniffer = HeaderAnomalySniffer(
                include_subresources=True,  # или False, если интересны только документы
                url_filter=lambda u: u.startswith(self.API_URL),
            )
            await sniffer.start(self.ctx)

            collected = {}

            def on_request(request):
                if request.url.startswith(self.API_URL):
                    collected[request.url] = request.headers

            self.ctx.on("request", on_request)

            await self.page.goto(self.MAIN_SITE_URL, wait_until="networkidle")
            await self.page.wait_for_selector("next-route-announcer", state="attached")
            await asyncio.sleep(1)
            await self.page.locator(
                'main a[data-qa^="sidebar-sub-category-"][data-qa$="-link"]'
            ).first.click()
            await self.page.locator(
                'main div[itemtype="https://schema.org/Product"]'
            ).first.click()
            await asyncio.sleep(1)
            await self.page.wait_for_load_state("load")

            await self.ctx.unroute("**/api/**", on_request)
            result_sniffer = await sniffer.complete()

            # Результат: {заголовок: [уникальные значения]}
            result = defaultdict(set)

            # Проходим по всем URL в 'request'
            for _url, headers in result_sniffer["request"].items():
                for header, values in headers.items():
                    result[header].update(
                        values
                    )  # добавляем значения, set уберёт дубли

            # Преобразуем set обратно в list
            self.unstandard_headers = {k: list(v)[0] for k, v in result.items()}
            self.unstandard_urls = collected

        await self.page.goto(f"{self.API_URL}/v1", wait_until="networkidle")

        await self.page.wait_for_selector(
            "pre", timeout=self.timeout_ms, state="attached"
        )

    async def __aexit__(self, *exc):
        """Выход из контекстного менеджера с закрытием сессии."""
        await self.close()

    async def close(self):
        """Закрыть HTTP-сессию и освободить ресурсы."""
        await self.session.close()

    async def _request(
        self,
        method: HttpMethod,
        url: str,
        *,
        json_body: Any | None = None,
    ) -> FetchResponse:
        """Выполнить HTTP-запрос через внутреннюю сессию.

        Единая точка входа для всех HTTP-запросов библиотеки.
        Добавляет к ответу объект Request для совместимости.

        Args:
            method: HTTP метод (GET, POST, PUT, DELETE и т.д.)
            url: URL для запроса
            json_body: Тело запроса в формате JSON (опционально)
        """
        resp: FetchResponse = await self.page.fetch(
            url=url,
            method=method,
            body=json_body,
            credentials="same-origin",
            mode="cors",
            timeout_ms=self.timeout_ms,
            referrer=self.MAIN_SITE_ORIGIN,
            headers={"Accept": "application/json, text/plain, */*"},
        )

        return resp
