# -*- coding: utf-8 -*-
"""
Tests for src/services/stock_code_utils.py
Covers: is_code_like, normalize_code - including exchange prefix handling.
"""

import pytest

from unittest.mock import patch

from src.services.stock_code_utils import (
    is_code_like,
    normalize_code,
    resolve_index_stock_code_for_analysis,
)


class TestIsCodeLike:
    # --- Plain digit codes ---
    def test_plain_6_digit(self):
        assert is_code_like("600519") is True

    def test_plain_5_digit(self):
        assert is_code_like("00700") is True

    def test_4_digit_rejected(self):
        assert is_code_like("6001") is False

    # --- Suffix format ---
    def test_suffix_sh(self):
        assert is_code_like("600519.SH") is True

    def test_suffix_sz(self):
        assert is_code_like("000001.SZ") is True

    def test_suffix_bj(self):
        assert is_code_like("920493.BJ") is True

    def test_suffix_bj_rejects_non_bse_base(self):
        assert is_code_like("600519.BJ") is False

    def test_suffix_lowercase(self):
        assert is_code_like("600519.sh") is True

    # --- HK suffix format ---
    def test_suffix_hk(self):
        assert is_code_like("00700.HK") is True

    def test_suffix_hk_lowercase(self):
        assert is_code_like("00700.hk") is True

    def test_suffix_hk_short_code(self):
        assert is_code_like("1810.HK") is True

    def test_suffix_hk_rejects_6_digit_base(self):
        assert is_code_like("600519.HK") is False

    def test_suffix_sh_rejects_5_digit_base(self):
        assert is_code_like("00700.SH") is False

    def test_suffix_a_share_rejects_wrong_exchange(self):
        assert is_code_like("600519.SZ") is False
        assert is_code_like("000001.SH") is False
        assert is_code_like("920748.SH") is False

    # --- Exchange prefix format (Issue #6 fix) ---
    def test_prefix_sh_upper(self):
        assert is_code_like("SH600519") is True

    def test_prefix_sh_lower(self):
        assert is_code_like("sh600519") is True

    def test_prefix_sz(self):
        assert is_code_like("SZ000001") is True

    def test_prefix_bj(self):
        assert is_code_like("BJ920493") is True

    def test_prefix_bj_rejects_non_bse_base(self):
        assert is_code_like("BJ600519") is False

    def test_prefix_hk(self):
        assert is_code_like("HK00700") is True

    def test_prefix_hk_lower(self):
        assert is_code_like("hk00700") is True

    def test_prefix_hk_short_code(self):
        assert is_code_like("HK700") is True

    def test_prefix_hk_rejects_6_digit_base(self):
        assert is_code_like("HK600519") is False

    def test_dotted_prefix_cn(self):
        assert is_code_like("SH.600519") is True
        assert is_code_like("SZ.000001") is True
        assert is_code_like("BJ.920493") is True

    def test_dotted_prefix_bj_rejects_non_bse_base(self):
        assert is_code_like("BJ.600519") is False

    def test_prefix_a_share_rejects_wrong_exchange(self):
        assert is_code_like("SH000001") is False
        assert is_code_like("SZ600519") is False
        assert is_code_like("SH920748") is False
        assert is_code_like("SH.920748") is False

    # --- US tickers ---
    def test_us_ticker(self):
        assert is_code_like("AAPL") is True

    def test_us_ticker_with_exchange(self):
        assert is_code_like("TSLA.O") is True

    # --- Negative cases ---
    def test_plain_text(self):
        assert is_code_like("贵州茅台") is False

    def test_empty(self):
        assert is_code_like("") is False

    def test_mixed_invalid(self):
        assert is_code_like("abc123") is False


class TestNormalizeCode:
    # --- Plain digit codes ---
    def test_plain_6_digit(self):
        assert normalize_code("600519") == "600519"

    def test_plain_5_digit(self):
        assert normalize_code("00700") == "00700"

    def test_whitespace_stripped(self):
        assert normalize_code("  600519  ") == "600519"

    # --- Suffix format ---
    def test_suffix_sh_strips(self):
        assert normalize_code("600519.SH") == "600519"

    def test_suffix_sz_strips(self):
        assert normalize_code("000001.SZ") == "000001"

    def test_suffix_bj_strips(self):
        assert normalize_code("920493.BJ") == "920493"

    def test_suffix_bj_rejects_non_bse_base(self):
        assert normalize_code("600519.BJ") is None

    def test_suffix_ss_strips(self):
        assert normalize_code("600000.SS") == "600000"

    def test_suffix_hk_strips(self):
        assert normalize_code("00700.HK") == "00700"

    def test_suffix_hk_lowercase_strips(self):
        assert normalize_code("00700.hk") == "00700"

    def test_suffix_hk_short_code_is_zero_padded(self):
        assert normalize_code("1810.HK") == "01810"

    def test_suffix_hk_rejects_6_digit_base(self):
        assert normalize_code("600519.HK") is None

    def test_suffix_sh_rejects_5_digit_base(self):
        assert normalize_code("00700.SH") is None

    def test_suffix_a_share_rejects_wrong_exchange(self):
        assert normalize_code("600519.SZ") is None
        assert normalize_code("000001.SH") is None
        assert normalize_code("920748.SH") is None

    # --- Exchange prefix format (Issue #6 fix) ---
    def test_prefix_sh_upper(self):
        assert normalize_code("SH600519") == "600519"

    def test_prefix_sh_lower(self):
        assert normalize_code("sh600519") == "600519"

    def test_prefix_sz(self):
        assert normalize_code("SZ000001") == "000001"

    def test_prefix_bj(self):
        assert normalize_code("BJ920493") == "920493"

    def test_prefix_bj_rejects_non_bse_base(self):
        assert normalize_code("BJ600519") is None

    def test_prefix_hk(self):
        assert normalize_code("HK00700") == "00700"

    def test_prefix_hk_lower(self):
        assert normalize_code("hk00700") == "00700"

    def test_prefix_hk_short_code_is_zero_padded(self):
        assert normalize_code("HK700") == "00700"

    def test_prefix_hk_rejects_6_digit_base(self):
        assert normalize_code("HK600519") is None

    def test_dotted_prefix_cn_strips(self):
        assert normalize_code("SH.600519") == "600519"
        assert normalize_code("SZ.000001") == "000001"
        assert normalize_code("BJ.920493") == "920493"

    def test_dotted_prefix_bj_rejects_non_bse_base(self):
        assert normalize_code("BJ.600519") is None

    def test_prefix_a_share_rejects_wrong_exchange(self):
        assert normalize_code("SH000001") is None
        assert normalize_code("SZ600519") is None
        assert normalize_code("SH920748") is None
        assert normalize_code("SH.920748") is None

    def test_bse_exchange_prefix_suffix_regression(self):
        assert normalize_code("920748.BJ") == "920748"
        assert normalize_code("BJ920748") == "920748"
        assert is_code_like("920748.BJ") is True
        assert is_code_like("BJ920748") is True
        assert normalize_code("bj920748") == "920748"
        assert is_code_like("bj.920748") is True

    # --- US tickers ---
    def test_us_ticker(self):
        assert normalize_code("AAPL") == "AAPL"

    # --- Invalid inputs ---
    def test_empty_returns_none(self):
        assert normalize_code("") is None

    def test_plain_text_returns_none(self):
        assert normalize_code("贵州茅台") is None

    def test_partial_prefix_no_digits_returns_none(self):
        # SH followed by wrong digit count
        assert normalize_code("SH6005") is None


class TestResolveIndexStockCodeForAnalysis:
    def test_resolves_via_stock_index(self):
        with patch("src.data.stock_index_loader.resolve_index_stock_code", return_value="005930.KS"):
            assert resolve_index_stock_code_for_analysis("005930") == "005930.KS"

    def test_resolves_indexed_4_digit_jp_base(self):
        assert is_code_like("7203") is False
        with patch("src.data.stock_index_loader.resolve_index_stock_code", return_value="7203.T"):
            assert resolve_index_stock_code_for_analysis("7203") == "7203.T"

    def test_falls_back_to_canonical_when_index_miss(self):
        with patch("src.data.stock_index_loader.resolve_index_stock_code", return_value=None):
            assert resolve_index_stock_code_for_analysis("005930") == "005930"
            assert resolve_index_stock_code_for_analysis("AAPL") == "AAPL"
