from __future__ import annotations

import math
import ssl
from urllib.parse import quote

import httpx
import structlog

from agrobr.http.settings import get_timeout
from agrobr.http.user_agents import UserAgentRotator
from agrobr.utils.geo import fetch_wfs, parse_wfs_hits

from .models import (
    MAX_FEATURES_GEO,
    PAGE_SIZE,
    PROPERTY_NAMES,
    PROPERTY_NAMES_GEO,
    WFS_BASE,
    WFS_VERSION,
    layer_name,
)

logger = structlog.get_logger()

TIMEOUT = get_timeout(read=180.0)

_ssl_ctx = ssl.create_default_context()
_ssl_ctx.set_ciphers("DEFAULT:@SECLEVEL=1")


def _build_wfs_url(
    uf: str,
    *,
    cql_filter: str | None = None,
    count: int = PAGE_SIZE,
    start_index: int = 0,
    result_type: str | None = None,
    output_format: str = "csv",
    property_names: list[str] | None = None,
) -> str:
    props = ",".join(property_names or PROPERTY_NAMES)
    layer = layer_name(uf)

    url = (
        f"{WFS_BASE}"
        f"?service=WFS&version={WFS_VERSION}&request=GetFeature"
        f"&typeNames=sicar:{layer}"
        f"&outputFormat={output_format}"
        f"&propertyName={props}"
        f"&count={count}"
        f"&startIndex={start_index}"
    )
    if result_type:
        url += f"&resultType={result_type}"
    if cql_filter:
        url += f"&CQL_FILTER={quote(cql_filter)}"
    return url


async def fetch_hits(
    uf: str,
    cql_filter: str | None = None,
    *,
    client: httpx.AsyncClient | None = None,
) -> int:
    url = _build_wfs_url(uf, cql_filter=cql_filter, result_type="hits")
    content = await fetch_wfs(url, source="sicar", timeout=TIMEOUT, client=client)
    return parse_wfs_hits(content, source="sicar")


async def fetch_imoveis(uf: str, cql_filter: str | None = None) -> tuple[list[bytes], str]:
    async with httpx.AsyncClient(
        timeout=TIMEOUT,
        headers=UserAgentRotator.get_bot_headers(),
        follow_redirects=True,
        verify=_ssl_ctx,
    ) as http:
        total = await fetch_hits(uf, cql_filter, client=http)
        logger.info("sicar_hits", uf=uf, total=total, cql_filter=cql_filter)

        if total == 0:
            url = _build_wfs_url(uf, cql_filter=cql_filter)
            return [], url

        n_pages = math.ceil(total / PAGE_SIZE)
        pages: list[bytes] = []
        base_url = _build_wfs_url(uf, cql_filter=cql_filter)

        for i in range(n_pages):
            start_index = i * PAGE_SIZE
            url = _build_wfs_url(
                uf,
                cql_filter=cql_filter,
                count=PAGE_SIZE,
                start_index=start_index,
            )
            delay = 2.0 if i >= 5 else None
            content = await fetch_wfs(
                url,
                source="sicar",
                timeout=TIMEOUT,
                base_delay=delay,
                client=http,
            )
            pages.append(content)
            logger.debug(
                "sicar_page",
                uf=uf,
                page=i + 1,
                total_pages=n_pages,
                size=len(content),
            )

    return pages, base_url


async def fetch_imoveis_geo(
    uf: str,
    cql_filter: str | None = None,
) -> tuple[bytes, str]:
    url = _build_wfs_url(
        uf,
        cql_filter=cql_filter,
        count=MAX_FEATURES_GEO,
        output_format="application/json",
        property_names=PROPERTY_NAMES_GEO,
    )
    async with httpx.AsyncClient(
        timeout=TIMEOUT,
        headers=UserAgentRotator.get_bot_headers(),
        follow_redirects=True,
        verify=_ssl_ctx,
    ) as http:
        content = await fetch_wfs(url, source="sicar", timeout=TIMEOUT, client=http)
    logger.info("sicar_imoveis_geojson", source="sicar", size=len(content), uf=uf)
    return content, url
