"""
permanent_subs.py
=================
영구구독(Permanent WS Subscriptions) 통합 테이블 — **국내(KR)·해외(US) 단일 소스**.

배경:
- 기존 ``env_config.PERMANENT_WS_CODES`` 는 콤마 문자열이라 **시장/거래소/심볼 메타를
  담지 못함.** 해외(QQQM 등)는 국내 WS(H0STCNT0)로는 못 받고, 거래소 코드가 필요하다.
- 이 테이블은 코드별로 ``market_type/exchange/symbol/tf_min`` 을 보관해
  - KR → 국내 실시간 WS(H0STCNT0)
  - US → 해외 실시간 WS(HDFSCNT0, tr_key=D{EXCD}{SYMBOL})
  로 각각 라우팅할 수 있게 한다.

호환:
- ``env_config.PERMANENT_WS_CODES`` 는 폴백으로 유지(테이블이 비어 있을 때만).
- ``migrate_env_codes()`` 로 기존 콤마 코드를 1회 이관(중복은 건너뜀).
"""
from __future__ import annotations

import logging
from typing import Any, Dict, List, Optional

logger = logging.getLogger("permanent_subs")

# 해외(US) 실시간 WS tr_key 거래소 코드 — 주문용(NASD/NYSE/AMEX) → 시세용(NAS/NYS/AMS)
_US_EXCD_MAP = {
    "NASD": "NAS", "NAS": "NAS",
    "NYSE": "NYS", "NYS": "NYS",
    "AMEX": "AMS", "AMS": "AMS",
}

_PERM_DDL = """
CREATE TABLE IF NOT EXISTS permanent_subscriptions (
    id          BIGINT AUTO_INCREMENT PRIMARY KEY,
    code        VARCHAR(16)  NOT NULL,
    market_type VARCHAR(8)   NOT NULL DEFAULT 'KR',
    exchange    VARCHAR(16)  NOT NULL DEFAULT 'KRX',
    symbol      VARCHAR(32)  NOT NULL DEFAULT '',
    tf_min      INT          NOT NULL DEFAULT 60,
    enabled     TINYINT      NOT NULL DEFAULT 1,
    note        VARCHAR(100) NOT NULL DEFAULT '',
    created_at  DATETIME     DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uq_perm_code (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='영구구독(국내 WS + 해외 WS) 종목'
"""


def _core(db: Any):
    """TradeDBExt 이면 ``.raw``, 아니면 그대로."""
    return getattr(db, "raw", db)


def ensure_permanent_subs_table(db: Any) -> None:
    raw = _core(db)
    raw.conn.execute(_PERM_DDL.strip())
    raw.conn.commit()


def classify_market(code: str) -> tuple[str, str, str]:
    """
    코드 형태로 (market_type, exchange, symbol) 추정.
    - 6자리 숫자 → KR/KRX
    - 영문 1~8자 → US/NASD (거래소는 이후 UI/DB에서 보정 가능)
    """
    c = str(code or "").strip().upper()
    if c.isdigit() and len(c) == 6:
        return "KR", "KRX", c
    if c.isalpha() and 1 <= len(c) <= 8:
        return "US", "NASD", c
    # 알 수 없으면 KR 취급(보수적)
    return "KR", "KRX", c


def us_ws_tr_key(exchange: str, symbol: str) -> str:
    """해외 실시간 WS tr_key — D + 시세거래소(NAS/NYS/AMS) + 심볼 (예: DNASQQQM)."""
    ex = str(exchange or "NASD").strip().upper()
    excd = _US_EXCD_MAP.get(ex, "NAS")
    sym = str(symbol or "").strip().upper()
    return f"D{excd}{sym}"


def upsert_permanent_sub(
    db: Any,
    code: str,
    market_type: Optional[str] = None,
    exchange: Optional[str] = None,
    symbol: Optional[str] = None,
    tf_min: int = 60,
    enabled: bool = True,
    note: str = "",
) -> None:
    """코드 1건 등록/갱신 (code UNIQUE)."""
    ensure_permanent_subs_table(db)
    code = str(code or "").strip().upper()
    if not code:
        raise ValueError("code 필수")
    g_mt, g_ex, g_sym = classify_market(code)
    mt = (str(market_type).strip().upper() if market_type else "") or g_mt
    ex = (str(exchange).strip().upper() if exchange else "") or (g_ex if mt == g_mt else ("KRX" if mt == "KR" else "NASD"))
    sym = (str(symbol).strip().upper() if symbol else "") or g_sym or code
    try:
        tfv = int(tf_min)
    except (TypeError, ValueError):
        tfv = 60
    if tfv < 1:
        tfv = 60
    raw = _core(db)
    raw.conn.execute(
        """
        INSERT INTO permanent_subscriptions
            (code, market_type, exchange, symbol, tf_min, enabled, note)
        VALUES (%s, %s, %s, %s, %s, %s, %s)
        ON DUPLICATE KEY UPDATE
            market_type=VALUES(market_type), exchange=VALUES(exchange),
            symbol=VALUES(symbol), tf_min=VALUES(tf_min),
            enabled=VALUES(enabled), note=VALUES(note)
        """,
        [code, mt, ex, sym, tfv, 1 if enabled else 0, str(note or "")[:100]],
    )
    raw.conn.commit()


def remove_permanent_sub(db: Any, code: str) -> bool:
    ensure_permanent_subs_table(db)
    code = str(code or "").strip().upper()
    if not code:
        return False
    raw = _core(db)
    cur = raw.conn.execute("DELETE FROM permanent_subscriptions WHERE code=%s", [code])
    raw.conn.commit()
    try:
        return bool(cur.rowcount)
    except Exception:
        return True


def set_enabled(db: Any, code: str, enabled: bool) -> None:
    ensure_permanent_subs_table(db)
    raw = _core(db)
    raw.conn.execute(
        "UPDATE permanent_subscriptions SET enabled=%s WHERE code=%s",
        [1 if enabled else 0, str(code or "").strip().upper()],
    )
    raw.conn.commit()


def list_permanent_subs(db: Any, enabled_only: bool = False) -> List[Dict[str, Any]]:
    ensure_permanent_subs_table(db)
    raw = _core(db)
    sql = "SELECT code, market_type, exchange, symbol, tf_min, enabled, note FROM permanent_subscriptions"
    if enabled_only:
        sql += " WHERE enabled=1"
    sql += " ORDER BY market_type, code"
    cur = raw.conn.execute(sql)
    rows = cur.fetchall() if cur else []
    out: List[Dict[str, Any]] = []
    for r in rows:
        d = dict(r) if not isinstance(r, dict) else dict(r)
        d["enabled"] = int(d.get("enabled", 1))
        out.append(d)
    return out


def codes_by_market(db: Any, market_type: str, enabled_only: bool = True) -> List[Dict[str, Any]]:
    """특정 시장(KR/US) 영구구독 목록 (WS 배선용)."""
    mt = str(market_type or "").strip().upper()
    return [r for r in list_permanent_subs(db, enabled_only=enabled_only)
            if str(r.get("market_type", "KR")).strip().upper() == mt]


def migrate_env_codes(db: Any, env_csv: str) -> int:
    """
    기존 ``PERMANENT_WS_CODES`` 콤마 문자열 → 테이블 1회 이관.
    이미 있는 code 는 건드리지 않음(덮어쓰기 방지). 신규 삽입 건수 반환.
    """
    ensure_permanent_subs_table(db)
    existing = {r["code"] for r in list_permanent_subs(db)}
    n = 0
    for raw_code in str(env_csv or "").split(","):
        c = raw_code.strip().upper()
        if not c or c in existing:
            continue
        mt, ex, sym = classify_market(c)
        upsert_permanent_sub(db, c, mt, ex, sym, note="env 이관")
        existing.add(c)
        n += 1
    return n
