"""ODsay LAB API 클라이언트 — 경로 검색, 정류장/역 검색, 노선 정보."""

from __future__ import annotations

import time
from typing import Any
from urllib.parse import quote

import httpx

from korea_transit_mcp_server.config import (
    CACHE_TTL_ROUTE,
    CACHE_TTL_STATION,
    cache,
    config,
)

BASE_URL = "https://api.odsay.com/v1/api"

# 일일 호출 횟수 추적 (ODsay 무료 요금제: 일 1,000콜)
_daily_calls: dict[str, int] = {"count": 0, "date": ""}
DAILY_LIMIT = 1000


def _check_rate_limit() -> None:
    """일일 호출 한도를 확인하고 초과 시 예외를 발생시킵니다."""
    today = time.strftime("%Y-%m-%d")
    # 날짜가 변했으면 카운터 초기화
    if _daily_calls["date"] != today:
        _daily_calls["count"] = 0
        _daily_calls["date"] = today
    # 일일 한도 초과 확인
    if _daily_calls["count"] >= DAILY_LIMIT:
        raise RuntimeError(
            f"ODsay 일일 호출 한도 ({DAILY_LIMIT}콜) 초과. 내일 다시 시도하세요."
        )
    _daily_calls["count"] += 1


async def _get(endpoint: str, params: dict[str, Any]) -> dict[str, Any]:
    """ODsay API에 GET 요청을 보내고 응답을 반환합니다."""
    if not config.odsay_api_key:
        raise RuntimeError("ODSAY_API_KEY 환경변수가 설정되지 않았습니다.")
    # 일일 호출 한도 확인
    _check_rate_limit()
    # API 요청에 필요한 파라미터 추가
    params["apiKey"] = config.odsay_api_key
    params["lang"] = params.get("lang", 0)  # 0=한글
    params["output"] = "json"
    # API 요청 전송
    async with httpx.AsyncClient(timeout=15) as client:
        resp = await client.get(f"{BASE_URL}/{endpoint}", params=params)
        resp.raise_for_status()
        data = resp.json()
    # 응답에 에러가 포함되어 있으면 예외 발생
    if "error" in data:
        raise RuntimeError(f"ODsay API 에러: {data['error']}")
    return data


# ---- 경로 검색 ----

async def search_pub_trans_path(
    sx: float, sy: float, ex: float, ey: float, search_type: int = 0
) -> dict[str, Any]:
    """두 좌표 사이의 대중교통 경로를 검색합니다.

    Args:
        sx, sy: 출발지 경도, 위도
        ex, ey: 도착지 경도, 위도
        search_type: 0=추천, 1=최소시간, 2=최소환승
    """
    # 경로 검색 결과 캐싱 (TTL 10분)
    cache_key = f"route:{sx},{sy}:{ex},{ey}:{search_type}"
    now = time.time()
    cached = cache.get(cache_key, now)
    if cached is not None:
        return cached
    data = await _get("searchPubTransPathT", {
        "SX": sx, "SY": sy, "EX": ex, "EY": ey,
        "SearchType": search_type,
    })
    cache.set(cache_key, data, CACHE_TTL_ROUTE, now)
    return data


# ---- 위치 검색 (주소/POI → 좌표) ----

async def search_point(keyword: str) -> dict[str, Any]:
    """키워드로 위치를 검색하여 좌표를 조회합니다."""
    # 위치 검색 결과 캐싱 (TTL 1일)
    cache_key = f"point:{keyword}"
    now = time.time()
    cached = cache.get(cache_key, now)
    if cached is not None:
        return cached
    data = await _get("searchStation", {
        "stationName": quote(keyword),
    })
    cache.set(cache_key, data, CACHE_TTL_STATION, now)
    return data


# ---- 정류장/역 검색 ----

async def search_station(keyword: str, station_class: int | None = None) -> dict[str, Any]:
    """키워드로 정류장 또는 역을 검색합니다.

    Args:
        keyword: 검색할 정류장/역 이름
        station_class: None=전체, 1=버스정류장, 2=지하철역
    """
    # 정류장 검색 결과 캐싱 (TTL 1일)
    cache_key = f"station:{keyword}:{station_class}"
    now = time.time()
    cached = cache.get(cache_key, now)
    if cached is not None:
        return cached
    # 검색어 URL 인코딩 및 필터 적용
    params: dict[str, Any] = {"stationName": quote(keyword)}
    if station_class is not None:
        params["stationClass"] = station_class
    data = await _get("searchStation", params)
    cache.set(cache_key, data, CACHE_TTL_STATION, now)
    return data


# ---- 주변 정류장/역 검색 ----

async def search_nearby_station(
    x: float, y: float, radius: int = 500
) -> dict[str, Any]:
    """좌표 기반으로 반경 내 주변 정류장/역을 검색합니다.

    Args:
        x, y: 경도, 위도
        radius: 검색 반경 (미터, 기본값 500)
    """
    # 주변 정류장 검색 결과 캐싱 (TTL 1일)
    cache_key = f"nearby:{x},{y}:{radius}"
    now = time.time()
    cached = cache.get(cache_key, now)
    if cached is not None:
        return cached
    data = await _get("pointSearch", {
        "x": x, "y": y, "radius": radius,
    })
    cache.set(cache_key, data, CACHE_TTL_STATION, now)
    return data


# ---- 노선/경로 정보 ----

async def load_lane(bus_local_blid: str | None = None, bus_no: str | None = None) -> dict[str, Any]:
    """버스 노선의 상세 정보를 조회합니다."""
    params: dict[str, Any] = {}
    # 버스 노선 ID 또는 번호 중 하나 입력
    if bus_local_blid:
        params["busLocalBlID"] = bus_local_blid
    if bus_no:
        params["busNo"] = bus_no
    return await _get("loadLane", params)


async def subway_station_info(station_name: str, city_code: int = 1000) -> dict[str, Any]:
    """지하철역의 정보와 연결된 노선을 조회합니다.

    Args:
        station_name: 역 이름
        city_code: 1000=서울 광역권
    """
    # 지하철역 정보 캐싱 (TTL 1일)
    cache_key = f"subway_info:{station_name}:{city_code}"
    now = time.time()
    cached = cache.get(cache_key, now)
    if cached is not None:
        return cached
    data = await _get("subwayStationInfo", {
        "stationName": quote(station_name),
        "CID": city_code,
    })
    cache.set(cache_key, data, CACHE_TTL_STATION, now)
    return data


async def load_line_info(lane_id: int) -> dict[str, Any]:
    """노선의 상세 정보를 조회합니다 (경유역, 시간표 등).

    Args:
        lane_id: ODsay 노선 ID
    """
    # 노선 정보 캐싱 (TTL 1일)
    cache_key = f"line_info:{lane_id}"
    now = time.time()
    cached = cache.get(cache_key, now)
    if cached is not None:
        return cached
    data = await _get("subwayPath", {"CID": 1000, "SID": lane_id, "EID": lane_id})
    cache.set(cache_key, data, CACHE_TTL_STATION, now)
    return data


def get_daily_call_count() -> int:
    """현재 일일 ODsay API 호출 횟수를 반환합니다."""
    today = time.strftime("%Y-%m-%d")
    # 날짜가 변했으면 0 반환
    if _daily_calls["date"] != today:
        return 0
    return _daily_calls["count"]
