from __future__ import annotations

from typing import Any, Literal

import pandas as pd
import structlog

from agrobr.datasets.base import BaseDataset, DatasetInfo, DatasetSource, _unpack_result
from agrobr.datasets.deterministic import get_snapshot
from agrobr.models import MetaInfo

logger = structlog.get_logger()


async def _fetch_bcb_odata(produto: str, **kwargs: Any) -> tuple[pd.DataFrame, MetaInfo | None]:
    from agrobr import bcb

    safra = kwargs.get("safra")
    finalidade = kwargs.get("finalidade", "custeio")
    uf = kwargs.get("uf")
    agregacao = kwargs.get("agregacao", "municipio")
    programa = kwargs.get("programa")
    tipo_seguro = kwargs.get("tipo_seguro")

    result = await bcb.credito_rural(
        produto,
        safra=safra,
        finalidade=finalidade,
        uf=uf,
        agregacao=agregacao,
        programa=programa,
        tipo_seguro=tipo_seguro,
        return_meta=True,
    )

    return _unpack_result(result)


CREDITO_RURAL_INFO = DatasetInfo(
    name="credito_rural",
    description="Crédito rural SICOR/BCB por UF ou município, com fallback BigQuery",
    sources=[
        DatasetSource(
            name="bcb",
            priority=1,
            fetch_fn=_fetch_bcb_odata,
            description="BCB API Olinda (OData) com fallback BigQuery",
        ),
    ],
    products=["soja", "milho", "arroz", "feijao", "trigo", "algodao", "cafe", "cana", "sorgo"],
    contract_version="1.1",
    update_frequency="monthly",
    typical_latency="M+1",
    source_url="https://olinda.bcb.gov.br",
    source_institution="BCB/SICOR",
    min_date="2013-01-01",
    unit="BRL",
    license="livre",
)


class CreditoRuralDataset(BaseDataset):
    info = CREDITO_RURAL_INFO

    async def fetch(  # type: ignore[override]
        self,
        produto: str,
        safra: str | None = None,
        finalidade: str = "custeio",
        uf: str | None = None,
        agregacao: Literal["municipio", "uf", "programa"] = "municipio",
        programa: str | None = None,
        tipo_seguro: str | None = None,
        return_meta: bool = False,
        **kwargs: Any,
    ) -> pd.DataFrame | tuple[pd.DataFrame, MetaInfo]:
        logger.info(
            "dataset_fetch",
            dataset="credito_rural",
            produto=produto,
            safra=safra,
            finalidade=finalidade,
        )

        snapshot = get_snapshot()
        if snapshot and safra is None:
            ano_snap = int(snapshot[:4])
            safra = f"{ano_snap - 1}/{ano_snap}"

        df, source_name, source_meta, attempted = await self._try_sources(
            produto,
            safra=safra,
            finalidade=finalidade,
            uf=uf,
            agregacao=agregacao,
            programa=programa,
            tipo_seguro=tipo_seguro,
            **kwargs,
        )

        df = self._normalize(df, produto, finalidade)
        self._validate_contract(df)

        if return_meta:
            return df, self._build_meta(df, source_name, source_meta, attempted, snapshot)

        return df

    def _normalize(self, df: pd.DataFrame, produto: str, finalidade: str) -> pd.DataFrame:
        if "produto" not in df.columns:
            df["produto"] = produto

        if "finalidade" not in df.columns:
            df["finalidade"] = finalidade

        return df


_credito_rural = CreditoRuralDataset()

from agrobr.datasets.registry import register  # noqa: E402

register(_credito_rural)


async def credito_rural(
    produto: str,
    safra: str | None = None,
    finalidade: str = "custeio",
    uf: str | None = None,
    agregacao: Literal["municipio", "uf", "programa"] = "municipio",
    programa: str | None = None,
    tipo_seguro: str | None = None,
    return_meta: bool = False,
    **kwargs: Any,
) -> pd.DataFrame | tuple[pd.DataFrame, MetaInfo]:
    return await _credito_rural.fetch(
        produto,
        safra=safra,
        finalidade=finalidade,
        uf=uf,
        agregacao=agregacao,
        programa=programa,
        tipo_seguro=tipo_seguro,
        return_meta=return_meta,
        **kwargs,
    )
