# -*- coding: utf-8 -*-
"""
“近期记录 / 历史记录(Former)”接口路由规则。

需求：
- 当请求参数包含可定义时间范围的字段时，根据当前时间（北京时间/上海时区）判断：
  - 3个月（自然月）之前 -> 使用“历史记录”接口
  - 3个月以内 -> 使用“近期记录”接口
  - 若时间范围跨越边界 -> 拆分为两段分别请求并合并（由上层分页迭代器顺序产出）
"""

from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime
from typing import Optional

from dateutil import parser as dtparser
from dateutil.relativedelta import relativedelta
from zoneinfo import ZoneInfo


ROUTING_TZ = ZoneInfo("Asia/Shanghai")
RECENT_MONTHS = 3


# 按 `fetch-test/recent_vs_former_report.md` 更新（“无”表示没有历史接口；相同 path 表示同一个接口可查历史）
RECENT_TO_FORMER_OVERRIDES: dict[str, str | None] = {
    "/AssistantPerformance/GetAbolitionAssistant": None,
    "/Site/GetSiteTableUseDetails": "/Site/GetSiteTableUseDetails",
    "/GoodsStockManage/QueryGoodsOutboundReceipt": "/GoodsStockManage/QueryFormerGoodsOutboundReceipt",
    "/Promotion/GetOfflineCouponConsumePageList": "/Promotion/GetOfflineCouponConsumePageList",
    "/Order/GetRefundPayLogList": None,
    # 已知特殊
    "/Site/GetAllOrderSettleList": "/Site/GetFormerOrderSettleList",
    "/PayLog/GetPayLogListPage": "/PayLog/GetFormerPayLogListPage",
}


TIME_WINDOW_KEYS: tuple[tuple[str, str], ...] = (
    ("startTime", "endTime"),
    ("rangeStartTime", "rangeEndTime"),
    ("StartPayTime", "EndPayTime"),
)


@dataclass(frozen=True)
class WindowSpec:
    start_key: str
    end_key: str
    start: datetime
    end: datetime


@dataclass(frozen=True)
class RoutedCall:
    endpoint: str
    params: dict


def is_former_endpoint(endpoint: str) -> bool:
    return "Former" in str(endpoint or "")


def _parse_dt(value: object, tz: ZoneInfo) -> datetime | None:
    if value is None:
        return None
    s = str(value).strip()
    if not s:
        return None
    dt = dtparser.parse(s)
    if dt.tzinfo is None:
        return dt.replace(tzinfo=tz)
    return dt.astimezone(tz)


def _fmt_dt(dt: datetime, tz: ZoneInfo) -> str:
    return dt.astimezone(tz).strftime("%Y-%m-%d %H:%M:%S")


def extract_window_spec(params: dict | None, tz: ZoneInfo = ROUTING_TZ) -> WindowSpec | None:
    if not isinstance(params, dict) or not params:
        return None
    for start_key, end_key in TIME_WINDOW_KEYS:
        if start_key in params or end_key in params:
            start = _parse_dt(params.get(start_key), tz)
            end = _parse_dt(params.get(end_key), tz)
            if start and end:
                return WindowSpec(start_key=start_key, end_key=end_key, start=start, end=end)
    return None


def derive_former_endpoint(recent_endpoint: str) -> str | None:
    endpoint = str(recent_endpoint or "").strip()
    if not endpoint:
        return None

    if endpoint in RECENT_TO_FORMER_OVERRIDES:
        return RECENT_TO_FORMER_OVERRIDES[endpoint]

    if is_former_endpoint(endpoint):
        return endpoint

    idx = endpoint.find("Get")
    if idx == -1:
        return endpoint
    return f"{endpoint[:idx]}GetFormer{endpoint[idx + 3:]}"


def recent_boundary(now: datetime, months: int = RECENT_MONTHS) -> datetime:
    """
    3个月（自然月）边界：取 (now - months) 所在月份的 1 号 00:00:00。
    """
    if now.tzinfo is None:
        raise ValueError("now 必须为时区时间")
    base = now - relativedelta(months=months)
    return base.replace(day=1, hour=0, minute=0, second=0, microsecond=0)


def plan_calls(
    endpoint: str,
    params: dict | None,
    *,
    now: datetime | None = None,
    tz: ZoneInfo = ROUTING_TZ,
    months: int = RECENT_MONTHS,
) -> list[RoutedCall]:
    """
    根据 endpoint + params 的时间窗口，返回要调用的 endpoint/params 列表（可能拆分为两段）。
    """
    base_params = dict(params or {})
    if not base_params:
        return [RoutedCall(endpoint=endpoint, params=base_params)]

    # 若调用方显式传了 Former 接口，则不二次路由。
    if is_former_endpoint(endpoint):
        return [RoutedCall(endpoint=endpoint, params=base_params)]

    window = extract_window_spec(base_params, tz)
    if not window:
        return [RoutedCall(endpoint=endpoint, params=base_params)]

    former_endpoint = derive_former_endpoint(endpoint)
    if former_endpoint is None or former_endpoint == endpoint:
        return [RoutedCall(endpoint=endpoint, params=base_params)]

    now_dt = (now or datetime.now(tz)).astimezone(tz)
    boundary = recent_boundary(now_dt, months=months)

    start, end = window.start, window.end
    if end <= boundary:
        return [RoutedCall(endpoint=former_endpoint, params=base_params)]
    if start >= boundary:
        return [RoutedCall(endpoint=endpoint, params=base_params)]

    # 跨越边界：拆分两段（老数据 -> former，新数据 -> recent）
    p1 = dict(base_params)
    p1[window.start_key] = _fmt_dt(start, tz)
    p1[window.end_key] = _fmt_dt(boundary, tz)

    p2 = dict(base_params)
    p2[window.start_key] = _fmt_dt(boundary, tz)
    p2[window.end_key] = _fmt_dt(end, tz)

    return [RoutedCall(endpoint=former_endpoint, params=p1), RoutedCall(endpoint=endpoint, params=p2)]

