from typing import Any, Dict, List, Tuple, Optional
from dataclasses import dataclass
from ..audit.schema import AuditEntry, AuditReport

# 说明文字用的 # 仅作解释 不包含代码语法标记
# 权威源固定为 世界银行 透明国际 IMF OECD
# 使用真实联网请求 返回可审计的 evidence 与可解析的值


# 轻量 http 工具
def _http_get_bytes(url: str, timeout: int = 12) -> bytes:
    import urllib.request
    req = urllib.request.Request(url, headers={"User-Agent": "ai-language-filter/0.2"})
    with urllib.request.urlopen(req, timeout=timeout) as r:
        return r.read()

def _http_get_text(url: str, timeout: int = 12) -> str:
    return _http_get_bytes(url, timeout).decode("utf-8", errors="replace")

def _http_get_json(url: str, timeout: int = 12) -> Any:
    import json
    data = _http_get_text(url, timeout)
    return json.loads(data)


@dataclass
class ApiSource:
    name: str
    endpoint: str

    def fetch(self, request: Dict[str, Any]) -> Dict[str, Any]:
        # 说明文字用的 # 该基类不直接使用
        raise NotImplementedError


# 世界银行
@dataclass
class WorldBankSource(ApiSource):
    name: str = "WorldBank"
    endpoint: str = "https://api.worldbank.org/v2"

    def fetch(self, request: Dict[str, Any]) -> Dict[str, Any]:
        # 说明文字用的 # 需要 country 与 indicator
        country = str(request.get("country", "")).strip() or str(request.get("country_iso2", "")).strip() or str(request.get("country_iso3", "")).strip()
        indicator = str(request.get("indicator", "")).strip()
        if not country or not indicator:
            return {"ok": False, "evidence": f"{self.name}::{self.endpoint}", "error": "missing country or indicator"}

        # 说明文字用的 # 取最近一期数据
        # 文档示例 v2 接口 支持 MRV 参数
        url = f"{self.endpoint}/country/{country}/indicator/{indicator}?format=json&per_page=1&MRV=1"
        try:
            js = _http_get_json(url)
            # 返回为列表 第一项是分页元信息 第二项是观测列表
            if isinstance(js, list) and len(js) >= 2 and isinstance(js[1], list) and js[1]:
                item = js[1][0]
                val = item.get("value", None)
                date = item.get("date", None)
                ok = val is not None
                evidence = f"{self.name}::{url}"
                return {"ok": bool(ok), "evidence": evidence, "value": val, "period": date}
            return {"ok": False, "evidence": f"{self.name}::{url}", "error": "no data"}
        except Exception as e:
            return {"ok": False, "evidence": f"{self.name}::{url}", "error": str(e)}


# 透明国际
@dataclass
class TransparencyInternationalSource(ApiSource):
    name: str = "TransparencyInternational"
    # 说明文字用的 # 使用公开 CSV 数据源
    # 该链接指向透明国际公开仓库中的 CPI 年度数据
    endpoint: str = "https://raw.githubusercontent.com/transparency-int/cpi/main/2023/cpi_2023_public.csv"

    def fetch(self, request: Dict[str, Any]) -> Dict[str, Any]:
        # 说明文字用的 # 建议传入 country 或 country_iso3
        country = str(request.get("country", "")).strip().lower()
        iso3 = str(request.get("country_iso3", "")).strip().upper()

        import csv
        import io

        url = self.endpoint
        try:
            text = _http_get_text(url)
            f = io.StringIO(text)
            reader = csv.DictReader(f)
            found_val: Optional[str] = None
            found_row: Optional[Dict[str, Any]] = None

            # 常见列名 cpi_score 或 CPI score 或 Score
            score_keys = ["cpi_score", "CPI score", "Score", "CPI 2023 score", "cpi", "value"]

            for row in reader:
                row_lower = {k.lower(): v for k, v in row.items()}
                name = row_lower.get("country", "") or row_lower.get("country_name", "")
                name_lc = str(name).strip().lower()
                row_iso3 = row_lower.get("iso3", "") or row_lower.get("iso3_code", "")
                row_iso3_up = str(row_iso3).strip().upper()

                match_name = bool(country and name_lc == country)
                match_iso3 = bool(iso3 and row_iso3_up == iso3)

                if match_name or match_iso3:
                    for key in score_keys:
                        if key in row or key in row_lower:
                            val = row.get(key) if key in row else row_lower.get(key)
                            if val is not None and str(val).strip() != "":
                                found_val = str(val).strip()
                                found_row = row
                                break
                if found_val:
                    break

            if found_val:
                evidence = f"{self.name}::{url}"
                return {"ok": True, "evidence": evidence, "value": found_val, "period": "latest"}
            return {"ok": False, "evidence": f"{self.name}::{url}", "error": "country not found"}
        except Exception as e:
            return {"ok": False, "evidence": f"{self.name}::{url}", "error": str(e)}


# IMF
@dataclass
class IMFSource(ApiSource):
    name: str = "IMF"
    # 说明文字用的 # 使用 SDMX JSON 接口
    endpoint: str = "https://dataservices.imf.org/REST/SDMX_JSON.svc/CompactData"

    def fetch(self, request: Dict[str, Any]) -> Dict[str, Any]:
        # 说明文字用的 # 需要 dataset 与 key
        # 示例 IFS 数据集 年频 Area 指标 例如 IFS A CN NGDP_R_PCH
        dataset = str(request.get("imf_dataset", "IFS")).strip()
        freq = str(request.get("imf_freq", "A")).strip()
        area = str(request.get("imf_area", request.get("country", ""))).strip().upper()
        indicator = str(request.get("imf_indicator", "")).strip().upper()
        if not area or not indicator:
            return {"ok": False, "evidence": f"{self.name}::{self.endpoint}", "error": "missing area or indicator"}

        key = f"{freq}.{area}.{indicator}"
        url = f"{self.endpoint}/{dataset}/{key}?contentType=json"
        try:
            js = _http_get_json(url)
            data = js.get("CompactData", {})
            ds = data.get("DataSet", {})
            series = ds.get("Series", {})
            # Series 可能是列表或字典
            obs_list = []
            if isinstance(series, list) and series:
                s0 = series[0]
                obs_list = s0.get("Obs", [])
            elif isinstance(series, dict):
                obs_list = series.get("Obs", [])

            if obs_list:
                # 取最后一个观测
                last = obs_list[-1]
                val = last.get("@OBS_VALUE") or last.get("OBS_VALUE") or last.get("value")
                time = last.get("@TIME_PERIOD") or last.get("TIME_PERIOD") or last.get("time")
                ok = val is not None
                evidence = f"{self.name}::{url}"
                return {"ok": bool(ok), "evidence": evidence, "value": val, "period": time}
            return {"ok": False, "evidence": f"{self.name}::{url}", "error": "no observations"}
        except Exception as e:
            return {"ok": False, "evidence": f"{self.name}::{url}", "error": str(e)}


# OECD
# 说明文字用的 #：OECD 源的 dataclass 修正，避免“non-default after default”
from dataclasses import dataclass, field
from typing import Dict

@dataclass
class ApiSource:
    name: str
    base_url: str
    timeout: int = 30
    headers: Dict[str, str] = field(default_factory=dict)

@dataclass
class OECDSource(ApiSource):
    # 关键修正：为 endpoint 提供默认值，或将其上移到所有默认字段之前
    endpoint: str = "https://stats.oecd.org/sdmx-json/data"
    detail_param: str = "detail=dataonly"
    dimension_param: str = "dimensionAtObservation=AllDimensions"

    # 其余方法不变，例如
    def build_url(self, dataset_key: str) -> str:
        return f"{self.endpoint}/{dataset_key}.JSON?{self.detail_param}&{self.dimension_param}"















# 说明文字用的 #：提供 CrossChecker 定义与默认构建器，供 core.py 导入；不依赖外部模块
from dataclasses import dataclass, field
from typing import Dict, List

@dataclass
class CrossChecker:
    # 非默认字段在前，默认值用 field(default_factory=...)，兼容 dataclass 规则
    sources: Dict[str, ApiSource] = field(default_factory=dict)

    def get(self, name: str) -> ApiSource:
        if name not in self.sources:
            raise KeyError(f"unknown source: {name}")
        return self.sources[name]

    def register(self, name: str, src: ApiSource) -> None:
        self.sources[name] = src

    def names(self) -> List[str]:
        return list(self.sources.keys())

# 说明文字用的 #：可选的默认注册器；CLI --help 通常不会用到，但便于交互测试
def build_default_crosschecker() -> CrossChecker:
    cc = CrossChecker()
    try:
        # 若你上文已将 OECDSource.endpoint 设默认值，则这里不必再传；否则按你的类签名传参
        cc.register(
            "oecd",
            OECDSource(
                name="oecd",
                base_url="https://stats.oecd.org",
                endpoint="https://stats.oecd.org/sdmx-json/data"
            )
        )
    except Exception:
        pass
    return cc

# 说明文字用的 #：确保导出 CrossChecker（若文件里已有 __all__，把以下符号合并进去）
try:
    __all__  # type: ignore
except NameError:
    __all__ = []
for _sym in ["ApiSource", "OECDSource", "CrossChecker", "build_default_crosschecker"]:
    if _sym not in __all__:
        __all__.append(_sym)
























    
