"""
Tests for the league catalog — archetypes, market taxonomy, discovery.
"""

import pytest
from altsportsdata.catalog import (
    Archetype,
    MarketInfo,
    LEAGUE_ARCHETYPES,
    LEAGUE_NAMES,
    MARKET_CATALOG,
    get_archetype,
    get_league_name,
    get_league_markets,
    get_market_info,
    format_league_description,
    format_catalog_table,
)


# ── Archetype classification ────────────────────────────────────────────────


class TestArchetypes:
    """All leagues are classified into the correct archetype."""

    def test_racing_leagues(self):
        racing = ["f1", "spr", "nrx", "motocrs", "motoamerica", "nhra",
                  "usac", "sprmtcrs", "hlrs", "worldoutlaws"]
        for league in racing:
            assert get_archetype(league) == Archetype.RACING, f"{league} should be racing"

    def test_heat_elimination_leagues(self):
        heat = ["wsl", "sls", "fdrift"]
        for league in heat:
            assert get_archetype(league) == Archetype.HEAT_ELIMINATION, f"{league} should be heat_elimination"

    def test_combat_leagues(self):
        combat = ["bkfc", "byb", "powerslap", "lux", "raf"]
        for league in combat:
            assert get_archetype(league) == Archetype.COMBAT, f"{league} should be combat"

    def test_team_based_leagues(self):
        team = ["masl", "nll", "athletesunlimited", "gsoc"]
        for league in team:
            assert get_archetype(league) == Archetype.TEAM_BASED, f"{league} should be team_based"

    def test_precision_leagues(self):
        precision = ["dgpt", "jaialai"]
        for league in precision:
            assert get_archetype(league) == Archetype.PRECISION, f"{league} should be precision"

    def test_multi_discipline(self):
        assert get_archetype("xgame") == Archetype.MULTI_DISCIPLINE

    def test_unknown_league_returns_none(self):
        assert get_archetype("unknown_league") is None

    def test_case_insensitive(self):
        assert get_archetype("F1") == Archetype.RACING  # .lower() normalizes
        assert get_archetype("f1") == Archetype.RACING

    def test_archetype_values_are_strings(self):
        for arch in Archetype:
            assert isinstance(arch.value, str)
            assert "_" not in arch.value or arch.value.replace("_", "").isalpha()


# ── League names ─────────────────────────────────────────────────────────────


class TestLeagueNames:
    def test_known_leagues_have_names(self):
        assert get_league_name("f1") == "Formula 1"
        assert get_league_name("wsl") == "World Surf League"
        assert get_league_name("bkfc") == "Bare Knuckle Fighting Championship"
        assert get_league_name("dgpt") == "Disc Golf Pro Tour"

    def test_unknown_league_returns_uppercased_key(self):
        assert get_league_name("xyz123") == "XYZ123"

    def test_all_archetyped_leagues_have_names(self):
        for key in LEAGUE_ARCHETYPES:
            name = get_league_name(key)
            assert name != key.upper(), f"{key} should have a proper name, got {name}"


# ── Market catalog ───────────────────────────────────────────────────────────


class TestMarketCatalog:
    def test_core_markets_exist(self):
        assert "eventWinner" in MARKET_CATALOG
        assert "headToHead" in MARKET_CATALOG
        assert "heatWinner" in MARKET_CATALOG

    def test_market_info_fields(self):
        info = MARKET_CATALOG["eventWinner"]
        assert isinstance(info, MarketInfo)
        assert info.api_name == "eventWinner"
        assert info.native_name == "moneyline"
        assert "get_moneylines" in info.method
        assert info.category == "core"
        assert len(info.description) > 0

    def test_all_markets_have_complete_info(self):
        for api_name, info in MARKET_CATALOG.items():
            assert info.api_name == api_name
            assert len(info.native_name) > 0, f"{api_name} missing native_name"
            assert len(info.method) > 0, f"{api_name} missing method"
            assert len(info.description) > 0, f"{api_name} missing description"
            assert info.category in ("core", "position", "prop", "exotic"), \
                f"{api_name} has invalid category: {info.category}"


class TestGetMarketInfo:
    def test_with_projections_suffix(self):
        info = get_market_info("eventWinnerProjections")
        assert info is not None
        assert info.native_name == "moneyline"

    def test_without_suffix(self):
        info = get_market_info("eventWinner")
        assert info is not None
        assert info.native_name == "moneyline"

    def test_head_to_head(self):
        info = get_market_info("headToHeadProjections")
        assert info is not None
        assert info.native_name == "matchup"

    def test_unknown_market(self):
        assert get_market_info("totallyFakeMarket") is None


class TestGetLeagueMarkets:
    def test_f1_markets(self):
        f1_raw = [
            "headToHeadProjections", "overUnderProjections",
            "multiOverUnderProjections", "trifectasEventProjections",
            "exactasEventProjections", "eventWinnerProjections",
            "raceTop3", "raceTop5", "raceTop10",
        ]
        markets = get_league_markets(f1_raw)
        names = [m.native_name for m in markets]
        assert "moneyline" in names
        assert "matchup" in names
        assert "top_3" in names
        assert "top_5" in names
        assert "top_10" in names

    def test_combat_league_markets(self):
        bkfc_raw = ["heatProjections"]
        markets = get_league_markets(bkfc_raw)
        names = [m.native_name for m in markets]
        assert "heat_winner" in names
        assert len(names) == 1

    def test_empty_markets(self):
        assert get_league_markets([]) == []

    def test_sorted_by_category(self):
        """Core markets should come before exotic markets."""
        wsl_raw = [
            "headToHeadProjections", "heatProjections",
            "eventWinnerProjections", "propBetProjections",
            "podiumProjections", "showsProjections",
        ]
        markets = get_league_markets(wsl_raw)
        categories = [m.category for m in markets]
        # Core should be first
        first_core = categories.index("core") if "core" in categories else 999
        last_position = len(categories) - 1 - categories[::-1].index("position") if "position" in categories else -1
        # All core before any exotic
        if "exotic" in categories:
            first_exotic = categories.index("exotic")
            assert first_core < first_exotic


# ── Format functions ─────────────────────────────────────────────────────────


class TestFormatLeagueDescription:
    def test_f1_description(self):
        output = format_league_description(
            "f1",
            "Formula 1",
            ["eventWinnerProjections", "headToHeadProjections", "overUnderProjections",
             "exactasEventProjections", "raceTop3", "raceTop5", "raceTop10"],
        )
        assert "Formula 1" in output
        assert "f1" in output
        assert "Racing" in output
        assert "get_moneylines" in output
        assert "get_matchups" in output
        assert "┌" in output  # box drawing

    def test_combat_description(self):
        output = format_league_description("bkfc", "BKFC", ["heatProjections"])
        assert "Combat" in output
        assert "get_heat_winners" in output

    def test_unknown_league(self):
        output = format_league_description("xyz", "Unknown Sport", [])
        assert "Unknown" in output
        assert "No markets configured" in output

    def test_includes_quick_start(self):
        output = format_league_description("wsl", "Surfing", ["eventWinnerProjections"])
        assert "Quick Start" in output
        assert 'get_league("wsl")' in output


class TestFormatCatalogTable:
    def test_basic_table(self):
        from altsportsdata.models import SportsListing
        leagues = [
            SportsListing(key="f1", name="Formula 1", supportedMarkets=["eventWinnerProjections", "headToHeadProjections"]),
            SportsListing(key="wsl", name="Surfing", supportedMarkets=["eventWinnerProjections", "heatProjections"]),
        ]
        output = format_catalog_table(leagues)
        assert "f1" in output
        assert "wsl" in output
        assert "Racing" in output
        assert "Heat Elimination" in output
        assert "2 leagues" in output


# ── Client integration (offline) ─────────────────────────────────────────────


class TestClientCatalogProperties:
    """Test catalog properties on the client without making API calls."""

    def test_archetype_property_no_league(self):
        from altsportsdata.client import AltSportsData
        # Can't instantiate without API key, so test the catalog functions directly
        assert get_archetype("f1") == Archetype.RACING
        assert get_archetype("wsl") == Archetype.HEAT_ELIMINATION

    def test_league_name_property(self):
        assert get_league_name("f1") == "Formula 1"
        assert get_league_name("wsl") == "World Surf League"

    def test_all_archetypes_covered(self):
        """Every archetype has at least one league."""
        covered = set()
        for arch in LEAGUE_ARCHETYPES.values():
            covered.add(arch)
        for arch in Archetype:
            assert arch in covered, f"Archetype {arch} has no leagues"


# ── Completeness checks ─────────────────────────────────────────────────────


class TestCatalogCompleteness:
    def test_minimum_leagues(self):
        """At least 25 leagues classified."""
        assert len(LEAGUE_ARCHETYPES) >= 25

    def test_minimum_markets(self):
        """At least 15 market types cataloged."""
        assert len(MARKET_CATALOG) >= 15

    def test_all_league_names(self):
        """Every classified league has a name."""
        for key in LEAGUE_ARCHETYPES:
            assert key in LEAGUE_NAMES, f"League {key} in archetypes but not in names"

    def test_native_names_unique(self):
        """No two markets share the same native name."""
        seen = set()
        for info in MARKET_CATALOG.values():
            assert info.native_name not in seen, f"Duplicate native name: {info.native_name}"
            seen.add(info.native_name)
