from __future__ import annotations

import tempfile
import unittest
from datetime import date, timedelta
from pathlib import Path

from hockey_data.client import Stat2TVNotFoundError, Stat2TVResponse
from hockey_data.config import HockeySettingsStore
from hockey_data.models import Tournament
from hockey_data.service import HockeyDataService
from support import LocalTestDatabase


class TournamentScheduleFallbackTests(unittest.IsolatedAsyncioTestCase):
    async def test_missing_live_schedule_uses_tournament_scoped_fallback(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            base_dir = Path(temp_dir)
            database = LocalTestDatabase(base_dir / "test.sqlite3")
            database.create_all()
            settings = HockeySettingsStore(base_dir / "settings")
            service = HockeyDataService(base_dir=base_dir, database=database, settings=settings)

            today = date.today()
            tomorrow = today + timedelta(days=1)
            with database.session() as session:
                session.add(Tournament(
                    external_id="1437",
                    name_ru="Предсезонные матчи КХЛ",
                    season="2026-2027",
                    start_date=today - timedelta(days=5),
                    end_date=today + timedelta(days=20),
                ))

            calls: list[str] = []
            fallback_xml = f'''<Schedule tournamentId="1437" tournamentType="tmp">
              <Game id="902918" date="{today.isoformat()}" time="15:00" teama="1" teamb="2" homeName="СКА" visitorName="Динамо" />
              <Game id="902919" date="{tomorrow.isoformat()}" time="18:00" teama="3" teamb="4" homeName="Локомотив" visitorName="Ак Барс" />
            </Schedule>'''.encode("utf-8")

            async def fake_fetch(endpoint: str) -> Stat2TVResponse:
                calls.append(endpoint)
                if endpoint == "1437/schedule-1437-live.xml":
                    raise Stat2TVNotFoundError("https://example.test/live", 404)
                if endpoint == "1437/schedule-1437.xml":
                    return Stat2TVResponse(
                        content=fallback_xml,
                        status_code=200,
                        content_type="application/xml",
                        auth_mode="test",
                        url=f"https://example.test/{endpoint}",
                    )
                raise Stat2TVNotFoundError(f"https://example.test/{endpoint}", 404)

            service.client.fetch_xml = fake_fetch  # type: ignore[method-assign]
            result = await service.sync_games(tournament_external_id="1437", on_date=today)

            self.assertTrue(result["fallback_used"])
            self.assertEqual(result["selected_date_count"], 1)
            self.assertEqual(result["endpoint"], "1437/schedule-1437.xml")
            self.assertEqual(calls[:2], [
                "1437/schedule-1437-live.xml",
                "1437/schedule-1437.xml",
            ])

            payload = service.games(tournament_external_id="1437", on_date=today)
            self.assertEqual(payload["meta"]["count"], 1)
            self.assertEqual(payload["items"][0]["external_id"], "902918")
            database.engine.dispose()


if __name__ == "__main__":
    unittest.main()

class TournamentScheduleDateQueryFallbackTests(unittest.IsolatedAsyncioTestCase):
    async def test_automatic_sync_reaches_root_date_query_candidates(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            base_dir = Path(temp_dir)
            database = LocalTestDatabase(base_dir / "test.sqlite3")
            database.create_all()
            settings = HockeySettingsStore(base_dir / "settings")
            service = HockeyDataService(base_dir=base_dir, database=database, settings=settings)

            selected = date(2026, 8, 12)
            with database.session() as session:
                session.add(Tournament(
                    external_id="1437",
                    name_ru="Предсезонные матчи КХЛ",
                    season="2026-2027",
                    start_date=date(2026, 7, 20),
                    end_date=date(2026, 9, 4),
                ))

            calls: list[str] = []
            payload = f'''<Schedule tournamentId="1437" tournamentType="tmp">
              <Game id="902918" date="{selected.isoformat()}" time="15:00" teama="1" teamb="2" homeName="СКА" visitorName="Динамо" />
            </Schedule>'''.encode("utf-8")

            target = f"games.xml?tournament=1437&date={selected.isoformat()}"

            async def fake_fetch(endpoint: str) -> Stat2TVResponse:
                calls.append(endpoint)
                if endpoint == target:
                    return Stat2TVResponse(
                        content=payload,
                        status_code=200,
                        content_type="application/xml",
                        auth_mode="test",
                        url=f"https://example.test/{endpoint}",
                    )
                raise Stat2TVNotFoundError(f"https://example.test/{endpoint}", 404)

            service.client.fetch_xml = fake_fetch  # type: ignore[method-assign]
            result = await service.sync_games(
                tournament_external_id="1437",
                on_date=selected,
                force_discovery=False,
            )

            self.assertTrue(result["fallback_used"])
            self.assertEqual(result["selected_date_count"], 1)
            self.assertEqual(result["endpoint"], target)
            self.assertIn(target, calls)
            # Confirm this happened during the normal automatic path, not only
            # force_discovery=True.
            self.assertGreater(calls.index(target), 0)

            visible = service.games(tournament_external_id="1437", on_date=selected)
            self.assertEqual(visible["meta"]["count"], 1)
            self.assertEqual(visible["items"][0]["external_id"], "902918")
            database.engine.dispose()
