# tests/test_ticker_utils.py

import pytest

from src.ticker_utils import (
    TickerFormatter,
    generate_strict_search_query,
    get_ticker_info,
    normalize_company_name,
    to_ibkr,
    to_yfinance,
)


@pytest.mark.parametrize(
    "raw_name, expected",
    [
        # Basic cases
        ("China Resources Beer (Holdings) Company Limited", "China Resources Beer"),
        ("Samsung Electronics Co., Ltd.", "Samsung Electronics"),
        ("Tencent Holdings Limited", "Tencent"),
        ("Apple Inc.", "Apple"),
        ("Nestlé S.A.", "Nestlé"),
        # Stacked suffixes
        ("ABC Group Holdings Ltd", "ABC"),
        ("XYZ Corporation Inc.", "XYZ"),
        # Parentheses removal - FIXED: function removes both parentheses AND suffixes
        ("Tencent Holdings (0700)", "Tencent"),  # Both (0700) and Holdings removed
        ("Samsung Electronics Co., Ltd. (005930)", "Samsung Electronics"),
        # Non-English
        ("腾讯控股有限公司", "腾讯控股有限公司"),  # Should not strip if no match
        ("삼성전자 주식회사", "삼성전자 주식회사"),
        # Edge cases
        ("Holdings", "Holdings"),  # Safety valve prevents over-stripping
        ("A B C", "A B C"),
        ("", ""),
        (None, ""),
        ("Ltd", "Ltd"),
        ("Company Limited", "Company"),
        # Multiple iterations
        ("Test Holdings Company Limited", "Test"),
        # Trailing comma after suffix removal (B2 fix: Cybozu, Inc. → Cybozu, → Cybozu)
        ("Cybozu, Inc.", "Cybozu"),
        ("Foo, Ltd.", "Foo"),
        ("Baz, Corp.", "Baz"),
    ],
)
def test_normalize_company_name(raw_name, expected):
    result = normalize_company_name(raw_name)
    assert result == expected, f"Expected '{expected}', got '{result}'"


def test_normalize_does_not_leave_trailing_comma():
    """Any name whose suffix removal would leave a trailing comma is cleaned up."""
    cases = [
        ("Cybozu, Inc.", "Cybozu"),
        ("Foo, Ltd.", "Foo"),
        ("Baz, Corp.", "Baz"),
    ]
    for raw, expected in cases:
        result = normalize_company_name(raw)
        assert not result.endswith(
            ","
        ), f"normalize_company_name({raw!r}) left trailing comma: {result!r}"
        assert result == expected, f"Expected {expected!r}, got {result!r}"


@pytest.mark.parametrize(
    "ticker, raw_name, topic, expected",
    [
        # Multi-word name -> quoted
        (
            "0700.HK",
            "China Resources Beer (Holdings) Company Limited",
            "revenue",
            '"China Resources Beer" 0700.HK revenue',
        ),
        (
            "005930.KS",
            "Samsung Electronics Co., Ltd.",
            "earnings",
            '"Samsung Electronics" 005930.KS earnings',
        ),
        # Single word -> unquoted
        ("AAPL", "Apple Inc.", "price", "Apple AAPL price"),
        # Non-English - FIXED: Chinese names without spaces are treated as single words (no quotes)
        ("0700.HK", "腾讯控股", "revenue", "腾讯控股 0700.HK revenue"),
        # Edge cases
        ("", "", "", "  "),
        ("TSLA", "Tesla", "", "Tesla TSLA "),
    ],
)
def test_generate_strict_search_query(ticker, raw_name, topic, expected):
    result = generate_strict_search_query(ticker, raw_name, topic)
    assert result == expected, f"Expected '{expected}', got '{result}'"


@pytest.mark.parametrize(
    "input_ticker, expected_normalized, expected_metadata_keys",
    [
        # Standard format
        (
            "NOVN.SW",
            "NOVN.SW",
            [
                "original",
                "symbol",
                "exchange_suffix",
                "exchange_name",
                "country",
                "ibkr_exchange",
                "format",
            ],
        ),
        (
            "7203.T",
            "7203.T",
            [
                "original",
                "symbol",
                "exchange_suffix",
                "exchange_name",
                "country",
                "ibkr_exchange",
                "format",
            ],
        ),
        # IBKR format
        (
            "NOVN:SWX",
            "NOVN.SW",
            [
                "original",
                "symbol",
                "exchange_suffix",
                "exchange_name",
                "country",
                "ibkr_exchange",
                "format",
            ],
        ),
        # Reuters format (assuming mapping exists)
        (
            "NOVN.N-CH",
            "NOVN.SW",
            [
                "original",
                "symbol",
                "exchange_suffix",
                "exchange_name",
                "country",
                "ibkr_exchange",
                "format",
            ],
        ),
        # Plain US
        (
            "AAPL",
            "AAPL",
            [
                "original",
                "symbol",
                "exchange_suffix",
                "exchange_name",
                "country",
                "ibkr_exchange",
                "format",
            ],
        ),
        # Unknown suffix
        (
            "TEST.XX",
            "TEST.XX",
            [
                "original",
                "symbol",
                "exchange_suffix",
                "exchange_name",
                "country",
                "ibkr_exchange",
                "format",
            ],
        ),
        # Invalid
        (
            "INVALID@",
            "INVALID@",
            [
                "original",
                "symbol",
                "exchange_suffix",
                "exchange_name",
                "country",
                "ibkr_exchange",
                "format",
            ],
        ),
        # Lowercase input
        (
            "novn.sw",
            "NOVN.SW",
            [
                "original",
                "symbol",
                "exchange_suffix",
                "exchange_name",
                "country",
                "ibkr_exchange",
                "format",
            ],
        ),
    ],
)
def test_normalize_ticker(input_ticker, expected_normalized, expected_metadata_keys):
    normalized, metadata = TickerFormatter.normalize_ticker(input_ticker)
    assert normalized == expected_normalized
    assert list(metadata.keys()) == expected_metadata_keys
    assert metadata["original"] == input_ticker.upper()
    assert metadata["format"] in [
        "standard",
        "ibkr",
        "reuters",
        "plain",
        "unknown",
        "invalid",
        "share_class",
    ]
    assert metadata["country"] != "Unknown" or metadata["format"] in [
        "unknown",
        "invalid",
    ]


@pytest.mark.parametrize(
    "input_ticker, expected_normalized, expected_format, expected_country, expected_exchange",
    [
        # Multi-dot → hyphen normalization (the core fix)
        ("NIL.B.ST", "NIL-B.ST", "standard", "Sweden", "Nasdaq Stockholm"),
        ("VOLV.A.ST", "VOLV-A.ST", "standard", "Sweden", "Nasdaq Stockholm"),
        ("HM.B.ST", "HM-B.ST", "standard", "Sweden", "Nasdaq Stockholm"),
        ("NOVO.B.CO", "NOVO-B.CO", "standard", "Denmark", "Nasdaq Copenhagen"),
        ("NOK.A.HE", "NOK-A.HE", "standard", "Finland", "Nasdaq Helsinki"),
        ("SALM.A.OL", "SALM-A.OL", "standard", "Norway", "Oslo Børs"),
        # Already-correct hyphenated form: unchanged, now properly recognised
        ("VOLV-B.ST", "VOLV-B.ST", "standard", "Sweden", "Nasdaq Stockholm"),
        ("NOVO-B.CO", "NOVO-B.CO", "standard", "Denmark", "Nasdaq Copenhagen"),
        # Previously-missing single-dot Scandinavian: now get correct metadata
        ("EQNR.OL", "EQNR.OL", "standard", "Norway", "Oslo Børs"),
        # Unknown suffix with two dots: must NOT fire (XX not in EXCHANGE_SUFFIXES)
        # Two-dot ticker with unknown suffix falls through all patterns → "invalid"
        ("SOME.THING.XX", "SOME.THING.XX", "invalid", "Unknown", "Unknown"),
        # Must-not-break cases
        ("BP.L", "BP.L", "standard", "UK", "London Stock Exchange"),
        ("NOVN.SW", "NOVN.SW", "standard", "Switzerland", "SIX Swiss Exchange"),
        ("7203.T", "7203.T", "standard", "Japan", "Tokyo Stock Exchange"),
        ("NOVN.N-CH", "NOVN.SW", "reuters", "Switzerland", "SIX Swiss Exchange"),
        ("AAPL", "AAPL", "plain", "United States", "US Exchange (assumed)"),
        # US share-class / ADR preferred shares (1-char unknown suffix → hyphen)
        ("PBR.A", "PBR-A", "share_class", "United States", "US Exchange (assumed)"),
        ("BRK.B", "BRK-B", "share_class", "United States", "US Exchange (assumed)"),
        ("BBD.A", "BBD-A", "share_class", "United States", "US Exchange (assumed)"),
        # Known single-char exchange codes must NOT become share-class
        ("BP.L", "BP.L", "standard", "UK", "London Stock Exchange"),  # L=London
        ("7203.T", "7203.T", "standard", "Japan", "Tokyo Stock Exchange"),  # T=Tokyo
        ("EQNR.V", "EQNR.V", "standard", "Canada", "TSX Venture Exchange"),  # V=TSX
    ],
)
def test_normalize_ticker_multidot(
    input_ticker,
    expected_normalized,
    expected_format,
    expected_country,
    expected_exchange,
):
    normalized, metadata = TickerFormatter.normalize_ticker(input_ticker)
    assert normalized == expected_normalized
    assert metadata["format"] == expected_format
    assert metadata["country"] == expected_country
    assert metadata["exchange_name"] == expected_exchange


@pytest.mark.parametrize(
    "input_ticker, expected",
    [
        ("NOVN.SW", "NOVN.SW"),
        ("NOVN:SWX", "NOVN.SW"),
        ("NOVN.N-CH", "NOVN.SW"),
        ("AAPL", "AAPL"),
        ("7203.T", "7203.T"),
        ("INVALID", "INVALID"),
        # Scandinavian multi-dot normalisation
        ("NIL.B.ST", "NIL-B.ST"),
        ("HM.B.ST", "HM-B.ST"),
        # Already-hyphenated form: pass through unchanged
        ("VOLV-B.ST", "VOLV-B.ST"),
        # Single-dot Scandinavian: unchanged symbol, now correctly tagged
        ("EQNR.OL", "EQNR.OL"),
        # US share-class / ADR preferred shares: dot → hyphen
        ("PBR.A", "PBR-A"),
        ("BRK.B", "BRK-B"),
        ("BBD.A", "BBD-A"),
        # Regression: known single-char exchange codes must NOT be treated as share-class
        ("BP.L", "BP.L"),  # L = London Stock Exchange
        ("7203.T", "7203.T"),  # T = Tokyo Stock Exchange
        ("EQNR.V", "EQNR.V"),  # V = TSX Venture Exchange
    ],
)
def test_to_yfinance(input_ticker, expected):
    result = to_yfinance(input_ticker)
    assert result == expected


@pytest.mark.parametrize(
    "input_ticker, expected",
    [
        ("NOVN.SW", "NOVN:SWX"),
        ("NOVN:SWX", "NOVN:SWX"),
        ("NOVN.N-CH", "NOVN:SWX"),
        ("AAPL", "AAPL:SMART"),
        ("7203.T", "7203:TSE"),
        ("INVALID", "INVALID:SMART"),
    ],
)
def test_to_ibkr(input_ticker, expected):
    result = to_ibkr(input_ticker)
    assert result == expected


@pytest.mark.parametrize(
    "input_ticker, expected_country, expected_exchange",
    [
        ("NOVN.SW", "Switzerland", "SIX Swiss Exchange"),
        ("7203.T", "Japan", "Tokyo Stock Exchange"),
        ("AAPL", "United States", "US Exchange (assumed)"),
        ("0700.HK", "Hong Kong", "Hong Kong Stock Exchange"),
        ("UNKNOWN.XX", "Unknown", "Unknown"),
        # Scandinavian exchanges — previously returned Unknown
        ("EQNR.OL", "Norway", "Oslo Børs"),
        ("VOLV-B.ST", "Sweden", "Nasdaq Stockholm"),
        ("NOVO-B.CO", "Denmark", "Nasdaq Copenhagen"),
        # US share-class / ADR preferred
        ("PBR.A", "United States", "US Exchange (assumed)"),
    ],
)
def test_get_ticker_info(input_ticker, expected_country, expected_exchange):
    info = get_ticker_info(input_ticker)
    assert info["country"] == expected_country
    assert info["exchange_name"] == expected_exchange


@pytest.mark.parametrize(
    "ticker, expected",
    [
        ("AAPL", False),  # US
        ("NOVN.SW", True),  # International
        ("7203.T", True),
        ("UNKNOWN.XX", True),  # Defaults to Unknown -> True
        ("MSFT", False),
        ("PBR.A", False),  # US share-class (ADR preferred) — not international
        ("BRK.B", False),  # US share-class (Berkshire B) — not international
    ],
)
def test_is_international(ticker, expected):
    result = TickerFormatter.is_international(ticker)
    assert result == expected
