"""整点强制清仓(FORCE_CLOSE_*):UI 标识与持仓倒计时."""
from __future__ import annotations

import os
import time
from datetime import datetime, timedelta
from typing import Any, Optional
from zoneinfo import ZoneInfo

FORCE_CLOSE_RESULT = "强制清仓"
FORCE_CLOSE_GRACE_MINUTES = 15


def app_timezone_name() -> str:
    return (os.getenv("APP_TIMEZONE") or os.getenv("TZ") or "Asia/Shanghai").strip() or "Asia/Shanghai"


def normalize_force_close_bj_hour(value: Any) -> int:
    try:
        h = int(value)
    except (TypeError, ValueError):
        return 0
    return max(0, min(23, h))


def _now_dt(*, now_ms: Optional[int] = None, tz_name: Optional[str] = None) -> datetime:
    tz = ZoneInfo(tz_name or app_timezone_name())
    if now_ms is None:
        return datetime.now(tz)
    return datetime.fromtimestamp(int(now_ms) / 1000, tz=tz)


def force_close_hour_label(bj_hour: Any) -> str:
    return f"{normalize_force_close_bj_hour(bj_hour):02d}:00"


def force_close_label(bj_hour: Any) -> str:
    return f"强制清仓 {force_close_hour_label(bj_hour)}"


def is_force_close_active_hour(
    bj_hour: Any,
    *,
    now_ms: Optional[int] = None,
    tz_name: Optional[str] = None,
    grace_minutes: int = FORCE_CLOSE_GRACE_MINUTES,
) -> bool:
    """当前是否处于整点强制清仓执行窗口(整点起 grace 分钟内)."""
    return is_force_close_executing(
        bj_hour,
        now_ms=now_ms,
        tz_name=tz_name,
        grace_minutes=grace_minutes,
    )


def is_force_close_executing(
    bj_hour: Any,
    *,
    now_ms: Optional[int] = None,
    tz_name: Optional[str] = None,
    grace_minutes: int = FORCE_CLOSE_GRACE_MINUTES,
) -> bool:
    hour = normalize_force_close_bj_hour(bj_hour)
    now = _now_dt(now_ms=now_ms, tz_name=tz_name)
    target = now.replace(hour=hour, minute=0, second=0, microsecond=0)
    if now < target:
        return False
    end = target + timedelta(minutes=max(1, int(grace_minutes)))
    return now < end


def parse_closed_at_dt(
    closed_at: Any,
    *,
    tz_name: Optional[str] = None,
) -> Optional[datetime]:
    if closed_at is None:
        return None
    text = str(closed_at).strip()
    if not text:
        return None
    tz = ZoneInfo(tz_name or app_timezone_name())
    for fmt in ("%Y-%m-%d %H:%M:%S", "%Y-%m-%d %H:%M", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%dT%H:%M"):
        try:
            return datetime.strptime(text, fmt).replace(tzinfo=tz)
        except ValueError:
            continue
    return None


def is_close_at_force_close_window(
    closed_at: Any,
    bj_hour: Any,
    *,
    grace_minutes: int = FORCE_CLOSE_GRACE_MINUTES,
    tz_name: Optional[str] = None,
) -> bool:
    """平仓时刻是否落在北京时间整点强制清仓窗口内."""
    dt = parse_closed_at_dt(closed_at, tz_name=tz_name)
    if dt is None:
        return False
    hour = normalize_force_close_bj_hour(bj_hour)
    if dt.hour != hour:
        return False
    return dt.minute < max(1, int(grace_minutes))


def infer_force_close_result(
    closed_at: Any,
    *,
    enabled: bool,
    bj_hour: Any,
    grace_minutes: int = FORCE_CLOSE_GRACE_MINUTES,
    tz_name: Optional[str] = None,
) -> Optional[str]:
    if not enabled:
        return None
    if is_close_at_force_close_window(
        closed_at, bj_hour, grace_minutes=grace_minutes, tz_name=tz_name
    ):
        return FORCE_CLOSE_RESULT
    return None


def coerce_force_close_result(
    result: Optional[str],
    closed_at: Any,
    *,
    enabled: bool,
    bj_hour: Any,
    miss_reason: Optional[str] = None,
    grace_minutes: int = FORCE_CLOSE_GRACE_MINUTES,
    tz_name: Optional[str] = None,
) -> tuple[str, str]:
    """同步平仓归类:整点窗口内优先记为强制清仓."""
    res = (result or "").strip()
    note = (miss_reason or "").strip()
    if res == FORCE_CLOSE_RESULT:
        return res, note
    fc = infer_force_close_result(
        closed_at,
        enabled=enabled,
        bj_hour=bj_hour,
        grace_minutes=grace_minutes,
        tz_name=tz_name,
    )
    if not fc:
        return res, note
    if not note:
        note = f"北京时间 {force_close_hour_label(bj_hour)} 整点风控清仓"
    return fc, note


def apply_force_close_display_result(
    result: Optional[str],
    closed_at: Any,
    *,
    enabled: bool,
    bj_hour: Any,
    grace_minutes: int = FORCE_CLOSE_GRACE_MINUTES,
    tz_name: Optional[str] = None,
) -> str:
    """展示层:外部平仓/手动平仓若落在整点窗口,显示为强制清仓."""
    res = (result or "").strip()
    if res == FORCE_CLOSE_RESULT:
        return res
    fc = infer_force_close_result(
        closed_at,
        enabled=enabled,
        bj_hour=bj_hour,
        grace_minutes=grace_minutes,
        tz_name=tz_name,
    )
    if fc and (res in ("", "外部平仓", "手动平仓") or res.startswith("外部平仓")):
        return fc
    return res


def compute_next_force_close_at_ms(
    *,
    bj_hour: Any,
    now_ms: Optional[int] = None,
    tz_name: Optional[str] = None,
) -> Optional[int]:
    """下一次强制清仓时刻(北京时间整点)的 epoch 毫秒."""
    hour = normalize_force_close_bj_hour(bj_hour)
    now = _now_dt(now_ms=now_ms, tz_name=tz_name)
    target = now.replace(hour=hour, minute=0, second=0, microsecond=0)
    if now >= target:
        target += timedelta(days=1)
    return int(target.timestamp() * 1000)


def force_close_remaining_seconds(
    close_at_ms: Any,
    *,
    now_ms: Optional[int] = None,
) -> Optional[int]:
    try:
        close_at = int(close_at_ms)
    except (TypeError, ValueError):
        return None
    now = int(now_ms if now_ms is not None else time.time() * 1000)
    return max(0, int((close_at - now) / 1000))


def format_force_close_countdown(seconds: Any, *, active: bool = False) -> str:
    if active:
        return "执行中"
    try:
        sec = max(0, int(seconds))
    except (TypeError, ValueError):
        return "--:--:--"
    h = sec // 3600
    m = (sec % 3600) // 60
    s = sec % 60
    return f"{h:02d}:{m:02d}:{s:02d}"


def build_force_close_state(
    enabled: bool,
    bj_hour: Any,
    *,
    now_ms: Optional[int] = None,
    tz_name: Optional[str] = None,
    has_active_positions: Optional[bool] = None,
) -> dict[str, Any]:
    """实例级强制清仓状态(模板 / API 共用)."""
    if not enabled:
        return {
            "enabled": False,
            "bj_hour": normalize_force_close_bj_hour(bj_hour),
            "hour_label": force_close_hour_label(bj_hour),
            "label": force_close_label(bj_hour),
            "next_at_ms": None,
            "remaining_sec": None,
            "countdown": "",
            "active": False,
        }
    hour = normalize_force_close_bj_hour(bj_hour)
    executing = is_force_close_executing(hour, now_ms=now_ms, tz_name=tz_name)
    active = executing and (has_active_positions is not False)
    next_at_ms = compute_next_force_close_at_ms(bj_hour=hour, now_ms=now_ms, tz_name=tz_name)
    rem = force_close_remaining_seconds(next_at_ms, now_ms=now_ms) if next_at_ms else None
    return {
        "enabled": True,
        "bj_hour": hour,
        "hour_label": force_close_hour_label(hour),
        "label": force_close_label(hour),
        "next_at_ms": next_at_ms,
        "remaining_sec": rem,
        "countdown": format_force_close_countdown(rem, active=active),
        "active": active,
    }


def force_close_template_context(
    enabled: bool,
    bj_hour: Any,
    *,
    now_ms: Optional[int] = None,
    tz_name: Optional[str] = None,
    has_active_positions: Optional[bool] = None,
) -> dict[str, dict[str, Any]]:
    return {
        "force_close": build_force_close_state(
            enabled,
            bj_hour,
            now_ms=now_ms,
            tz_name=tz_name,
            has_active_positions=has_active_positions,
        )
    }


def apply_force_close_to_payload(
    payload: dict[str, Any],
    *,
    enabled: bool,
    bj_hour: Any,
    now_ms: Optional[int] = None,
    tz_name: Optional[str] = None,
) -> None:
    """为 active 持仓 JSON 附加整点强制清仓倒计时."""
    state = build_force_close_state(
        enabled,
        bj_hour,
        now_ms=now_ms,
        tz_name=tz_name,
        has_active_positions=True,
    )
    payload["force_close_enabled"] = bool(state["enabled"])
    payload["force_close_bj_hour"] = state["bj_hour"]
    payload["force_close_at_ms"] = state["next_at_ms"]
    payload["force_close_label"] = state["label"] if state["enabled"] else ""
    payload["force_close_remaining_sec"] = state["remaining_sec"]
    payload["force_close_countdown"] = state["countdown"]
    payload["force_close_active"] = bool(state["active"])


def enrich_orders_force_close(
    orders: list[dict[str, Any]],
    enabled: bool,
    bj_hour: Any,
    *,
    now_ms: Optional[int] = None,
    tz_name: Optional[str] = None,
) -> None:
    if not enabled or not orders:
        return
    for item in orders:
        if isinstance(item, dict):
            apply_force_close_to_payload(
                item,
                enabled=enabled,
                bj_hour=bj_hour,
                now_ms=now_ms,
                tz_name=tz_name,
            )
