# -*- coding: utf-8 -*-
"""
宏观择时 · 取数与落表 (严格单表访问)
======================================
两类访问, 都走 153 代理 (source="proxy"):
  1. 三张行情源表只读 —— 表位置与列名由 2026-08-18 标定实跑钉死
     (报告 MACRO_CALIB_2026-08-18.md 第一节, 改列名先改那份依据再改这里):
       zs_day_data   上证日线      symbol='000001.SH', 列 timestamp / close
       gp_fx_daily   美元兑离岸人民币  ts_code='USDCNH.FXCM', 列 trade_date / bid_close
       gp_shibor     拆借利率宽表   列 trade_date / rate_1m (单位百分数)
  2. 信号历史表 pms_macro_signal 读写 —— 每信号每交易日一行, 重扫就地更新 (唯一键)。

计算不在这里 (core/macro_rules.py), 决策与下命令也不在这里 (services/macro_service.py)。
"""
from __future__ import annotations

import json
import logging
from datetime import datetime

from app.db.session import execute, fetch_all, fetch_one

logger = logging.getLogger("pms.macro_repo")

# 源表口径 (标定钉死)
ZS_SYMBOL = "000001.SH"
FX_TSCODE = "USDCNH.FXCM"


def _dumps(v):
    return json.dumps(v, ensure_ascii=False) if not isinstance(v, (str, type(None))) else v


def _loads(v):
    if v in (None, ""):
        return {}
    try:
        return json.loads(v) if isinstance(v, str) else dict(v)
    except (TypeError, ValueError):
        return {}


# ================================================================ 行情源表 (只读)
def fetch_hedge_inputs(limit_days: int = 140) -> dict:
    """股汇对冲指数的三条原始序列, 各取最近 N 条, 返回升序 (ymd, value) 列表。

    N 默认 140 = 收益窗 20 + 标准化窗 40 + 富余; 单表查询, 符合代理的单表纪律。
    任一查询失败向上抛 (DBUnavailable), 由 macro_service 按「不可用 → 不动作」处理。
    """
    n = max(80, int(limit_days))
    zs = fetch_all(
        "SELECT `timestamp` AS d, `close` AS v FROM zs_day_data "
        "WHERE symbol = :s ORDER BY `timestamp` DESC LIMIT :n",
        {"s": ZS_SYMBOL, "n": n}, source="proxy")
    fx = fetch_all(
        "SELECT trade_date AS d, bid_close AS v FROM gp_fx_daily "
        "WHERE ts_code = :c ORDER BY trade_date DESC LIMIT :n",
        {"c": FX_TSCODE, "n": n}, source="proxy")
    sh = fetch_all(
        "SELECT trade_date AS d, rate_1m AS v FROM gp_shibor "
        "ORDER BY trade_date DESC LIMIT :n",
        {"n": n}, source="proxy")

    def _ser(rows):
        out = []
        for r in rows:
            if r.get("v") is None:
                continue
            out.append((r["d"], float(r["v"])))
        out.reverse()                      # DESC 取回 → 升序返回
        return out

    return {"zs": _ser(zs), "fx": _ser(fx), "shibor": _ser(sh)}


# ================================================================ pms_macro_signal
def upsert_signal(*, signal_key: str, trade_date: int, value, zone: str,
                  detail=None, action: str = "NONE", ref_id: str = "",
                  note: str = "") -> int:
    """写当日信号行; 同 (信号, 交易日) 重扫就地更新 (唯一键 uk_sig_date)。"""
    return execute(
        "INSERT INTO pms_macro_signal (signal_key, trade_date, value, zone, detail_json, "
        "action, ref_id, note, updated_at) "
        "VALUES (:k, :d, :v, :z, :dj, :a, :r, :nt, :ts) "
        "ON DUPLICATE KEY UPDATE value = :v, zone = :z, detail_json = :dj, "
        "action = :a, ref_id = :r, note = :nt, updated_at = :ts",
        {"k": signal_key, "d": int(trade_date), "v": value, "z": zone,
         "dj": _dumps(detail or {}), "a": action or "NONE", "r": ref_id or "",
         "nt": (note or "")[:500], "ts": datetime.now()},
        source="proxy")


def get_signal(signal_key: str, trade_date: int):
    r = fetch_one(
        "SELECT * FROM pms_macro_signal WHERE signal_key = :k AND trade_date = :d "
        "ORDER BY id DESC LIMIT 1",
        {"k": signal_key, "d": int(trade_date)}, source="proxy")
    if r:
        r["detail"] = _loads(r.get("detail_json"))
    return r


def recent_signals(signal_key: str, limit: int = 30) -> list:
    """最近的信号行, 按交易日从新到旧。detail_json 已解析进 detail 键。"""
    rows = fetch_all(
        "SELECT * FROM pms_macro_signal WHERE signal_key = :k "
        "ORDER BY trade_date DESC LIMIT :n",
        {"k": signal_key, "n": max(1, int(limit))}, source="proxy")
    for r in rows:
        r["detail"] = _loads(r.get("detail_json"))
    return rows
