import asyncio
import hrequests
from scrapy.http import HtmlResponse, Response
from scrapy.utils.defer import deferred_from_coro
from scrapy.exceptions import NotConfigured


class HRequestsDownloadHandler:
    """
    Scrapy download handler powered by hrequests.

    Features:
    - HTTP/2
    - TLS fingerprinting
    - Proxy support
    - Session reuse
    """

    def __init__(self, settings, crawler=None):
        if not settings.getbool("HREQUESTS_ENABLED", True):
            raise NotConfigured("HRequests handler disabled via settings")

        self.settings = settings
        self.sessions = {}  # proxy -> session

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler.settings, crawler)

    async def _get_session(self, proxy=None):
        """
        Reuse session per proxy.
        """
        if proxy not in self.sessions:
            self.sessions[proxy] = hrequests.AsyncSession(
                proxy=proxy
            )
        return self.sessions[proxy]

    async def _fetch(self, request, spider):
        proxy = request.meta.get("proxy")

        session = await self._get_session(proxy)

        try:
            resp = await session.request(
                method=request.method,
                url=request.url,
                headers=request.headers.to_unicode_dict(),
                data=request.body if request.body else None,
                cookies=request.cookies,
                timeout=self.settings.getint("HREQUESTS_TIMEOUT", 30),
                allow_redirects=False,
            )

        except Exception as e:
            spider.logger.error(f"hrequests error: {e}")
            raise

        return HtmlResponse(
            url=str(resp.url),
            status=resp.status_code,
            headers=resp.headers,
            body=resp.content,
            request=request,
            encoding=resp.encoding,
        )

    def download_request(self, request, spider):
        return deferred_from_coro(self._fetch(request, spider))

    async def close(self):
        for session in self.sessions.values():
            await session.close()
