"""
비본주(우선주·ETF·ETN·스팩) 판별 — 후보 매수 차단 공용.

키움 조건검색은 종목명 없이 code 만 주는 경우가 많아(name==code),
이름 끝 '우' 만으로는 우선주를 못 막는다. 코드 끝자리 규칙으로 보완한다.

KRX 단축코드 끝자리(종목구분):
  - 보통주: 대개 '0'
  - 우선주: '5','7','9' (구형) + 'K','L','M'… (2013년 이후 알파벳)
"""
from __future__ import annotations

from typing import FrozenSet

from .env import get_env_bool, get_env_from_db

# ETF/ETN 브랜드·유형 (이름 부분일치). '파워' 단독은 뉴파워프라즈마 오탐 → 넣지 않음.
_DEFAULT_NON_STOCK_NAME_KEYWORDS = (
    "ETN",
    "ETF",
    "레버리지",
    "인버스",
    "2X",
    "3X",
    "선물",
    "KODEX",
    "TIGER",
    "KBSTAR",
    "ARIRANG",
    "HANARO",
    "SOL ",
    "KOSEF",
    "ACE ",
    "KINDEX",
    "RISE ",
    "PLUS ",
    "TIMEFOLIO",
    "히어로",
    "SMART",
    "TREX",
    "WON",
    "KOACT",
)


def _pref_digit_suffixes() -> FrozenSet[str]:
    """우선주 숫자 끝자리 — NON_STOCK_PREF_CODE_SUFFIXES (기본 5,7,9)."""
    raw = get_env_from_db("NON_STOCK_PREF_CODE_SUFFIXES", "5,7,9") or "5,7,9"
    return frozenset(s.strip().upper() for s in str(raw).split(",") if s.strip())


def is_preferred_code(code: str) -> bool:
    """종목코드만으로 우선주 여부 (name 없어도 판단)."""
    c = (code or "").strip()
    if len(c) < 1:
        return False
    last = c[-1].upper()
    if last in _pref_digit_suffixes():
        return True
    # K,L,M… 알파벳 끝 = 신형 우선주 코드
    if get_env_bool("NON_STOCK_EXCLUDE_ALPHA_SUFFIX", True) and last.isalpha():
        return True
    return False


def is_etn_code(code: str) -> bool:
    c = (code or "").strip()
    return len(c) == 6 and c[0] == "7"


def _name_keywords() -> tuple:
    """NON_STOCK_NAME_KEYWORDS — 콤마 구분. 비우면 기본 튜플."""
    raw = (get_env_from_db("NON_STOCK_NAME_KEYWORDS", "") or "").strip()
    if not raw:
        return _DEFAULT_NON_STOCK_NAME_KEYWORDS
    parts = tuple(s.strip() for s in raw.split(",") if s.strip())
    return parts or _DEFAULT_NON_STOCK_NAME_KEYWORDS


def _name_hits_keyword(name: str) -> bool:
    nm = name.upper()
    for k in _name_keywords():
        if not k:
            continue
        if k.isascii():
            if k.upper() in nm:
                return True
        elif k in name:
            return True
    return False


def is_non_stock(name: str, code: str) -> bool:
    """ETN/ETF/스팩/우선주 등 매수 금지 종목이면 True.

    name 이 비었거나 name==code 여도 코드 규칙으로 우선주·ETN 을 잡는다.
    """
    code = (code or "").strip()
    name = (name or "").strip()

    if not code:
        return True

    if is_preferred_code(code):
        return True
    if is_etn_code(code):
        return True

    if name:
        if _name_hits_keyword(name):
            return True
        nm = name.upper()
        if "스팩" in name or "SPAC" in nm:
            return True
        # 우선주 표기 (우 / 우B / 우C …)
        if name.endswith("우") or "우B" in name or "우C" in name or name.endswith("(전환)"):
            return True

    return False


def is_unmanageable_holding(name: str, code: str) -> bool:
    """보유 동기화에서 빼서 봇이 매도하지 않을 종목 (시세 불가 ETF/ETN·스팩).

    우선주는 시세·매도 가능 → False. (실수로 산 우선주는 매도로 정리)
    """
    code = (code or "").strip()
    name = (name or "").strip()
    if is_etn_code(code):
        return True
    if not name or name == code:
        return False
    nm = name.upper()
    for k in ("ETN", "ETF", "레버리지", "인버스", "KODEX", "TIGER", "KBSTAR"):
        if k.isascii():
            if k in nm:
                return True
        elif k in name:
            return True
    if "스팩" in name or "SPAC" in nm:
        return True
    return False
