"""Mock TAB API client for testing without API access.

Provides realistic sample data for development and testing when TAB API
access is not available or for faster testing.
"""

import asyncio
import hashlib
from datetime import datetime, timedelta
from typing import Any

from packages.core.common.logging import get_logger
from packages.core.common.settings import get_settings

logger = get_logger(__name__)


class MockTABClient:
    """Mock TAB API client that returns sample data."""

    def __init__(self):
        """Initialize mock client."""
        self.settings = get_settings()
        logger.info("Initialized MockTABClient - using sample data instead of real API")

    async def __aenter__(self):
        """Async context manager entry."""
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        """Async context manager exit."""
        await self.close()

    async def close(self):
        """Close client (no-op for mock)."""
        pass

    def _generate_deterministic_id(self, seed: str) -> str:
        """Generate a deterministic ID from a seed string.

        This ensures the same input always produces the same ID,
        making tests reproducible.

        Args:
            seed: String to hash

        Returns:
            8-character hex string ID
        """
        return hashlib.md5(seed.encode()).hexdigest()[:8]

    async def get_meetings(
        self,
        date_from: str,
        date_to: str,
        category: str | None = None,
        country: str | None = None,
    ) -> list[dict[str, Any]]:
        """Return mock meeting data matching TAB API structure.

        Args:
            date_from: Start date (YYYY-MM-DD)
            date_to: End date (YYYY-MM-DD)
            category: Racing category filter ("T", "H", "G")
            country: Country filter

        Returns:
            List of mock meeting dictionaries
        """
        category = category or self.settings.tab.default_category
        country = country or self.settings.tab.default_country

        logger.info(
            f"[MOCK] Fetching {category} meetings from {date_from} to {date_to}"
        )

        # Simulate API delay
        await asyncio.sleep(0.1)

        # Parse dates
        start_date = datetime.strptime(date_from, "%Y-%m-%d")
        end_date = datetime.strptime(date_to, "%Y-%m-%d")

        # Venue names by category
        venues = {
            "H": ["Auckland", "Cambridge", "Addington", "Alexandra Park"],
            "T": ["Ellerslie", "Trentham", "Riccarton", "Hastings"],
            "G": ["Manukau", "Addington", "Wanganui", "Palmerston North"],
        }

        # Generate one meeting per day in range
        meetings = []
        current_date = start_date

        while current_date <= end_date:
            # Create 1-2 meetings per day
            venue_list = venues.get(category, venues["H"])
            num_meetings = 1 + (current_date.day % 2)

            for venue_idx in range(num_meetings):
                venue = venue_list[venue_idx % len(venue_list)]
                meeting_seed = f"{current_date.isoformat()}-{venue}-{category}"
                meeting_id = self._generate_deterministic_id(meeting_seed)

                # Generate 8 races per meeting
                races = []
                for race_num in range(1, 9):
                    event_seed = f"{meeting_id}-R{race_num}"
                    event_id = self._generate_deterministic_id(event_seed)
                    races.append(
                        {
                            "id": event_id,
                            "race_number": race_num,
                            "name": f"Race {race_num}",
                            "distance": 2000 + (race_num - 1) * 200,
                            "country": country,
                        }
                    )

                meetings.append(
                    {
                        "meeting": meeting_id,
                        "name": venue,
                        "date": f"{current_date.isoformat()}T00:00:00+13:00",
                        "meeting_date": current_date.strftime("%Y%m%d"),
                        "category": category,
                        "category_name": {
                            "H": "Harness",
                            "T": "Thoroughbreds",
                            "G": "Greyhounds",
                        }.get(category, "Harness"),
                        "country": country,
                        "state": "NZL",
                        "track_condition": "Good",
                        "races": races,
                    }
                )

            current_date += timedelta(days=1)

        logger.info(f"[MOCK] Returning {len(meetings)} mock meetings")
        return meetings

    async def get_meeting(self, meeting_id: str) -> dict[str, Any]:
        """Return mock meeting details with races.

        Args:
            meeting_id: Meeting ID

        Returns:
            Mock meeting dictionary with races array
        """
        logger.info(f"[MOCK] Fetching meeting {meeting_id}")

        # Simulate API delay
        await asyncio.sleep(0.05)

        # Generate 8 races
        races = []
        for race_num in range(1, 9):
            event_seed = f"{meeting_id}-R{race_num}"
            event_id = self._generate_deterministic_id(event_seed)
            races.append(
                {
                    "id": event_id,
                    "race_number": race_num,
                    "name": f"Race {race_num}",
                    "distance": 2000 + (race_num - 1) * 200,
                    "country": "NZ",
                }
            )

        return {
            "meeting": meeting_id,
            "name": "Auckland",
            "date": "2024-12-26T00:00:00+13:00",
            "meeting_date": "20241226",
            "category": "H",
            "category_name": "Harness",
            "country": "NZ",
            "state": "NZL",
            "track_condition": "Good",
            "races": races,
        }

    async def get_event(self, event_id: str) -> dict[str, Any]:
        """Return mock event details with runners and results.

        Args:
            event_id: Event ID

        Returns:
            Mock event dictionary with race, runners, and results
        """
        logger.info(f"[MOCK] Fetching event {event_id}")

        # Simulate API delay
        await asyncio.sleep(0.05)

        # Deterministically generate race number from event_id
        race_number = (int(event_id[:2], 16) % 8) + 1

        # Generate 8-12 runners with realistic data
        num_runners = 8 + (int(event_id[2:4], 16) % 5)
        runners = []
        results = []

        # Sample driver and trainer names for variety
        driver_names = [
            "John Smith",
            "Mary Jones",
            "David Brown",
            "Sarah Wilson",
            "Michael Taylor",
            "Emma Davis",
            "James Anderson",
            "Lucy Martin",
            "Robert White",
            "Emily Jackson",
            "William Harris",
            "Sophie Clark",
        ]
        trainer_names = [
            "Tom Mitchell",
            "Jane Roberts",
            "Peter Thompson",
            "Lisa Walker",
            "Mark Lewis",
            "Amy Young",
            "Chris Hall",
            "Karen Allen",
        ]

        for i in range(1, num_runners + 1):
            # Deterministic horse ID based on event and position
            horse_seed = f"{event_id}-horse-{i}"
            horse_id = (
                int(hashlib.md5(horse_seed.encode()).hexdigest()[:8], 16) % 100000
            )

            # Check if scratched (deterministic)
            is_scratched = i == num_runners and num_runners > 10

            # Driver and trainer names (deterministic selection)
            driver_idx = (int(event_id[:2], 16) + i) % len(driver_names)
            trainer_idx = (int(event_id[2:4], 16) + i) % len(trainer_names)

            runner = {
                "entrant_id": f"entrant-{event_id}-{i}",
                "horse_id": horse_id,
                "name": f"Mock Horse {i}",
                "runner_number": i,
                "barrier": i,
                "barrier_position": f"{i}{'F' if i <= 8 else 'B'}",  # Harness-specific
                "handicap": (i - 1) * 10,
                "jockey": driver_names[
                    driver_idx
                ],  # TAB uses "jockey" even for harness
                "trainer_name": trainer_names[trainer_idx],
                "trainer_location": "Auckland",
                "is_scratched": is_scratched,
                "is_late_scratched": False,
                "favourite": i == 1,
                "mover": False,
                "age": 4 + (i % 4),
                "sex": "G" if i % 2 == 0 else "M",
                "colour": "Brown",
                "sire": f"Sire {i}",
                "dam": f"Dam {i}",
                "breeding": f"Sire {i} x Dam {i}",
                "last_twenty_starts": "12345678x0",
                "prize_money": str(10000 + i * 1000),
                "gear": "",
                "silk_colours": "Blue and White",
                "silk_url_64x64": "",
                "silk_url_128x128": "",
                "class_level": "C1",
                "apprentice_indicator": "",
                "allowance_weight": "",
                "owners": f"Owner {i}",
                "country": "NZ",
                "weight": {"allocated": 0, "carried": 0},
                "form_indicators": [],
                "market_name": "Win",
                "primary_market": True,
                "scratch_time": 0,
                "odds": {"win": 3.0 + i * 0.5, "place": 1.5 + i * 0.2},
            }
            runners.append(runner)

            # Generate results for non-scratched runners
            if not is_scratched and i <= 8:
                results.append(
                    {
                        "entrant_id": f"entrant-{event_id}-{i}",
                        "position": i,
                        "name": f"Mock Horse {i}",
                        "barrier": i,
                        "runner_number": i,
                        "margin_length": 0 if i == 1 else (i - 1) * 1.5,
                    }
                )

        # Determine gait and start type based on race number
        gait = "Trot" if race_number % 2 == 0 else "Pace"
        start_type = "Mobile" if race_number % 3 != 0 else "Standing"

        race_data = {
            "event_id": event_id,
            "meeting_id": f"meeting-{event_id[:4]}",
            "meeting_name": "Auckland",
            "display_meeting_name": "Auckland",
            "race_number": race_number,
            "description": f"Mock Race {race_number}",
            "status": "Resulted",
            "type": "Harness",
            "country": "NZ",
            "state": "NZL",
            "distance": 2000 + (race_number - 1) * 200,
            "start_type": start_type,
            "gait": gait,
            "advertised_start": 1703570400,  # Unix timestamp
            "advertised_start_string": f"2024-12-26T{13 + race_number}:00:00+13:00",
            "actual_start": 1703570400,
            "actual_start_string": f"2024-12-26T{13 + race_number}:00:00+13:00",
            "weather": "Fine",
            "track_condition": "Good",
            "track_direction": "Right",
            "track_home_straight": 250.0,
            "rail_position": "True",
            "entrant_count": num_runners,
            "positions_paid": 3,
            "form_guide": "",
            "comment": "",
            "silk_base_url": "",
            "silk_url": "",
            "group": "",
            "tips": [],
            "prize_monies": {"1": 10000, "2": 3000, "3": 1500},
        }

        return {
            "race": race_data,
            "runners": runners,
            "results": results,
            "dividends": [],
            "derivatives": [],
            "favourite": runners[0] if runners else None,
            "mover": None,
            "big_bets": [],
            "live_bets": [],
            "error": "",
        }

    async def get_races_list(
        self,
        date_from: str,
        date_to: str,
        meet_types: str | None = None,
        countries: str | None = None,
        limit: int = 100,
    ) -> dict[str, Any]:
        """Return mock race list.

        Args:
            date_from: Start date (YYYY-MM-DD)
            date_to: End date (YYYY-MM-DD)
            meet_types: Racing type filter
            countries: Country filter
            limit: Maximum results

        Returns:
            Mock race list response
        """
        logger.info(f"[MOCK] Fetching race list from {date_from} to {date_to}")

        # Get meetings first, then extract races
        meetings = await self.get_meetings(date_from, date_to, meet_types, countries)

        races = []
        for meeting in meetings:
            for race in meeting.get("races", []):
                races.append(
                    {
                        **race,
                        "meeting_id": meeting["meeting"],
                        "meeting_name": meeting["name"],
                        "category": meeting["category"],
                    }
                )

        return {
            "races": races[:limit],
            "page_token": None,  # No pagination in mock
        }
