"""
KAP(한국자산평가) 등급별 회사채 기준수익률 수집 모듈

KAP 웹사이트(koreaap.com)에서 공모회사채(무보증) 1년 만기 등급별
기준수익률과 국고채 수익률을 수집합니다.

데이터 출처:
  - https://www.koreaap.com/kor/valuation01_01.html
  - 채권금리 기준수익률 > 공모회사채 (무보증)

사용법:
  ytm_data = get_ytm_data("2025-12-31")
  # {'rf': 2.550, 'AAA': 2.986, 'AA+': 3.045, ...}
"""

import logging
from typing import Dict, Optional

logger = logging.getLogger(__name__)


# ============================================================
# 하드코딩 Fallback 데이터
# KAP 스크래핑이 불가능할 경우 사용하는 기준 데이터
# ============================================================

# 2025-12-31 기준 KAP 공모회사채(무보증) 1년 만기 기준수익률 (%)
FALLBACK_YTM_20251231: Dict[str, float] = {
    "rf":   2.550,   # 국고채 1Y
    "AAA":  2.986,
    "AA+":  3.045,
    "AA":   3.094,
    "AA-":  3.132,
    "A+":   3.269,
    "A":    3.439,
    "A-":   3.704,
    "BBB+": 4.749,
    "BBB":  5.442,
    "BBB-": 6.442,
}


def get_ytm_data(
    date: str = "2025-12-31",
    use_fallback: bool = True
) -> Dict[str, float]:
    """
    KAP 등급별 1Y 회사채 수익률 + 국고채 수익률 반환

    Parameters
    ----------
    date : str
        기준일 (YYYY-MM-DD). 기본값 2025-12-31
    use_fallback : bool
        True이면 하드코딩 fallback 데이터 사용 (스크래핑 없이)

    Returns
    -------
    Dict[str, float]
        {'rf': 국고채1Y, 'AAA': AAA_YTM, 'AA+': ..., ...}
        단위: % (예: 2.986)
    """
    if use_fallback or date == "2025-12-31":
        logger.info(f"KAP YTM fallback 데이터 사용 (기준일: {date})")
        return FALLBACK_YTM_20251231.copy()

    # 향후 KAP 웹 스크래핑 구현 가능
    # try:
    #     return _scrape_kap(date)
    # except Exception as e:
    #     logger.warning(f"KAP 스크래핑 실패, fallback 사용: {e}")
    #     return FALLBACK_YTM_20251231.copy()

    logger.info(f"KAP YTM fallback 데이터 사용 (기준일: {date})")
    return FALLBACK_YTM_20251231.copy()


def compute_spreads(ytm_data: Dict[str, float]) -> Dict[str, float]:
    """
    등급별 신용스프레드 계산 (= 회사채 YTM - 국고채 YTM)

    Parameters
    ----------
    ytm_data : Dict[str, float]
        get_ytm_data() 반환값

    Returns
    -------
    Dict[str, float]
        등급별 스프레드 (단위: bp, 1bp = 0.01%)
        예: {'AAA': 43.6, 'AA+': 49.5, ...}
    """
    rf = ytm_data["rf"]
    spreads = {}
    for grade, ytm in ytm_data.items():
        if grade == "rf":
            continue
        spreads[grade] = round((ytm - rf) * 100, 1)  # % → bp
    return spreads


# ============================================================
# 대등급 매핑
# ============================================================

# 노치 → 대등급 매핑
NOTCH_TO_BROAD = {
    "AAA": "AAA",
    "AA+": "AA", "AA": "AA", "AA-": "AA",
    "A+": "A", "A": "A", "A-": "A",
    "BBB+": "BBB", "BBB": "BBB", "BBB-": "BBB",
    "BB+": "BB", "BB": "BB", "BB-": "BB",
    "B+": "B", "B": "B", "B-": "B",
}


def compute_broad_grade_spreads(
    spreads: Dict[str, float],
    method: str = "mid"
) -> Dict[str, float]:
    """
    노치등급 스프레드를 대등급(AAA, AA, A, BBB)으로 집계

    Parameters
    ----------
    spreads : Dict[str, float]
        노치등급별 스프레드 (bp)
    method : str
        'mid': 중간 노치 값 사용 (AA+ AA AA- → AA 값)
        'mean': 노치 평균

    Returns
    -------
    Dict[str, float]
        대등급별 스프레드 (bp)
    """
    from collections import defaultdict
    groups = defaultdict(list)

    for notch, spread in spreads.items():
        broad = NOTCH_TO_BROAD.get(notch, notch)
        groups[broad].append(spread)

    result = {}
    for broad, values in groups.items():
        if method == "mid":
            # 중간 노치 (3개면 가운데, 1개면 그대로)
            result[broad] = sorted(values)[len(values) // 2]
        else:
            result[broad] = sum(values) / len(values)

    return result


if __name__ == "__main__":
    # 테스트 실행
    ytm = get_ytm_data("2025-12-31")
    print("=== KAP 1Y YTM (2025-12-31) ===")
    for grade, rate in ytm.items():
        print(f"  {grade:>5}: {rate:.3f}%")

    print("\n=== 신용스프레드 (bp) ===")
    spreads = compute_spreads(ytm)
    for grade, sp in spreads.items():
        print(f"  {grade:>5}: {sp:.1f}bp")

    print("\n=== 대등급 스프레드 (bp) ===")
    broad = compute_broad_grade_spreads(spreads)
    for grade, sp in broad.items():
        print(f"  {grade:>5}: {sp:.1f}bp")
