"""Mapper to convert HRNZ scraped data to TipSharks data models."""

import hashlib
from datetime import datetime
from typing import Any

from packages.core.common.logging import get_logger

logger = get_logger(__name__)


class HRNZDataMapper:
    """Maps HRNZ scraped data to TipSharks database format.

    Converts the dictionaries returned by HRNZScraper into the format
    expected by TipSharks repositories for database insertion.
    """

    @staticmethod
    def map_meeting(scraped_meeting: dict[str, Any]) -> dict[str, Any]:
        """Map scraped meeting data to TipSharks meeting format.

        Args:
            scraped_meeting: Meeting dict from HRNZScraper

        Returns:
            Meeting dict compatible with MeetingRepository.upsert()
        """
        # Generate a meeting ID from date + venue
        # Format similar to TAB: hash of venue + date
        date_str = scraped_meeting.get("date", "")
        venue = scraped_meeting.get("venue", "Unknown")

        # Create deterministic ID from venue + date
        meeting_id_seed = f"{venue}_{date_str}"
        meeting_id = hashlib.md5(meeting_id_seed.encode()).hexdigest()[:8]

        meeting = {
            "meeting": meeting_id,  # Use "meeting" key for compatibility with TAB format
            "date": date_str,  # Keep ISO format string
            "name": venue,  # Use "name" key for compatibility with TAB format
            "category": "H",  # Harness racing
            "raw_json": scraped_meeting,  # Store original scraped data
        }

        return meeting

    @staticmethod
    def map_races(
        scraped_meeting: dict[str, Any], meeting_id: str
    ) -> list[dict[str, Any]]:
        """Map scraped races to TipSharks race format.

        Args:
            scraped_meeting: Meeting dict from HRNZScraper
            meeting_id: TipSharks meeting ID

        Returns:
            List of race dicts compatible with RaceRepository.upsert()
        """
        races = []

        scraped_races = scraped_meeting.get("races", [])
        meeting_date_str = scraped_meeting.get("date", "")

        for scraped_race in scraped_races:
            race = {
                "meeting_id": meeting_id,
                "race_number": scraped_race.get("race_number", 0),
                "distance_m": scraped_race.get("distance_m", 2000),
                "start_type": scraped_race.get("start_type", "Standing"),
                "name": scraped_race.get("name", ""),
                "weather": scraped_race.get("weather"),
                "track_condition": scraped_race.get("track_condition"),
                "raw_json": scraped_race,  # Store original scraped data
            }

            # Construct race datetime (use meeting date at noon if no time given)
            if meeting_date_str:
                try:
                    # Default to 12:00 PM if no specific time
                    race_datetime = datetime.fromisoformat(
                        f"{meeting_date_str}T12:00:00"
                    )
                    # Add race number offset (30 min per race)
                    race_offset_minutes = (race["race_number"] - 1) * 30
                    from datetime import timedelta

                    race_datetime += timedelta(minutes=race_offset_minutes)
                    # Store as ISO string for JSON serialization
                    race["race_datetime"] = race_datetime.isoformat()
                except ValueError as e:
                    logger.warning(f"Could not parse race datetime: {e}")

            # Determine gait (default to Pace for harness racing)
            race["gait"] = "Pace"  # HRNZ doesn't always specify, default to pace

            races.append(race)

        return races

    @staticmethod
    def map_starters(
        scraped_meeting: dict[str, Any],
        race_id_map: dict[int, int],
    ) -> list[dict[str, Any]]:
        """Map scraped starters to TipSharks starter format.

        Args:
            scraped_meeting: Meeting dict from HRNZScraper
            race_id_map: Mapping of race_number to database race ID

        Returns:
            List of starter dicts compatible with StarterRepository.upsert()
        """
        starters = []

        scraped_races = scraped_meeting.get("races", [])

        for scraped_race in scraped_races:
            race_number = scraped_race.get("race_number")
            race_id = race_id_map.get(race_number)

            if not race_id:
                logger.warning(
                    f"No database race_id for race {race_number}, skipping starters"
                )
                continue

            scraped_starters = scraped_race.get("starters", [])

            for scraped_starter in scraped_starters:
                # Map horse ID - convert UUID to integer within PostgreSQL INTEGER range
                horse_uuid = scraped_starter.get("horse_id")
                horse_name = scraped_starter.get("horse_name", "Unknown")
                if horse_uuid:
                    horse_id = (
                        int(hashlib.md5(horse_uuid.encode()).hexdigest()[:8], 16)
                        % 2147483647
                    )
                else:
                    horse_id = (
                        int(hashlib.md5(horse_name.encode()).hexdigest()[:8], 16)
                        % 2147483647
                    )

                # Map driver ID - convert UUID to integer within PostgreSQL INTEGER range
                driver_uuid = scraped_starter.get("driver_id")
                driver_name = scraped_starter.get("driver_name", "Unknown")
                if driver_uuid:
                    driver_id = (
                        int(hashlib.md5(driver_uuid.encode()).hexdigest()[:8], 16)
                        % 2147483647
                    )
                else:
                    driver_id = (
                        int(hashlib.md5(driver_name.encode()).hexdigest()[:8], 16)
                        % 2147483647
                    )

                # Map trainer ID - convert UUID to integer within PostgreSQL INTEGER range
                trainer_uuid = scraped_starter.get("trainer_id")
                trainer_name = scraped_starter.get("trainer_name", "Unknown")
                if trainer_uuid:
                    trainer_id = (
                        int(hashlib.md5(trainer_uuid.encode()).hexdigest()[:8], 16)
                        % 2147483647
                    )
                else:
                    trainer_id = (
                        int(hashlib.md5(trainer_name.encode()).hexdigest()[:8], 16)
                        % 2147483647
                    )

                starter = {
                    "race_id": race_id,
                    "horse_id": horse_id,
                    "name": horse_name,  # Add "name" field for StarterRepository compatibility
                    "driver_name": driver_name,
                    "trainer_name": trainer_name,
                    "driver_id": driver_id,
                    "trainer_id": trainer_id,
                    "runner_number": scraped_starter.get("runner_number"),
                    "barrier": scraped_starter.get("barrier", 1),
                    "barrier_position": scraped_starter.get("barrier_position"),
                    "handicap_m": scraped_starter.get("handicap_m", 0),
                    "placing": scraped_starter.get("placing"),
                    "did_not_finish": bool(scraped_starter.get("did_not_finish")),
                    "raw_json": scraped_starter,
                }

                starters.append(starter)

                # Also track entities for upserting
                # Return entity dicts for horses, drivers, trainers

        return starters

    @staticmethod
    def map_entities(
        scraped_meeting: dict[str, Any],
    ) -> dict[str, list[dict[str, Any]]]:
        """Extract and map all entities (horses, drivers, trainers).

        Args:
            scraped_meeting: Meeting dict from HRNZScraper

        Returns:
            Dict with 'horses', 'drivers', 'trainers' lists
        """
        horses = {}
        drivers = {}
        trainers = {}

        scraped_races = scraped_meeting.get("races", [])

        for scraped_race in scraped_races:
            scraped_starters = scraped_race.get("starters", [])

            for scraped_starter in scraped_starters:
                # Horse - convert UUID to integer within PostgreSQL INTEGER range
                horse_uuid = scraped_starter.get("horse_id")
                horse_name = scraped_starter.get("horse_name", "Unknown")
                if horse_uuid:
                    # Convert UUID to integer using hash (modulo to fit in PostgreSQL INTEGER)
                    horse_id = (
                        int(hashlib.md5(horse_uuid.encode()).hexdigest()[:8], 16)
                        % 2147483647
                    )
                else:
                    # Generate from horse name
                    horse_id = (
                        int(hashlib.md5(horse_name.encode()).hexdigest()[:8], 16)
                        % 2147483647
                    )

                if horse_id not in horses:
                    horses[horse_id] = {
                        "id": horse_id,
                        "name": horse_name,
                        "raw_json": {"source": "hrnz_scraper", "uuid": horse_uuid},
                    }

                # Driver - convert UUID to integer within PostgreSQL INTEGER range
                driver_uuid = scraped_starter.get("driver_id")
                driver_name = scraped_starter.get("driver_name", "Unknown")
                if driver_uuid:
                    driver_id = (
                        int(hashlib.md5(driver_uuid.encode()).hexdigest()[:8], 16)
                        % 2147483647
                    )
                else:
                    driver_id = (
                        int(hashlib.md5(driver_name.encode()).hexdigest()[:8], 16)
                        % 2147483647
                    )

                if driver_id not in drivers:
                    drivers[driver_id] = {
                        "id": driver_id,
                        "name": driver_name,
                        "raw_json": {"source": "hrnz_scraper", "uuid": driver_uuid},
                    }

                # Trainer - convert UUID to integer within PostgreSQL INTEGER range
                trainer_uuid = scraped_starter.get("trainer_id")
                trainer_name = scraped_starter.get("trainer_name", "Unknown")
                if trainer_uuid:
                    trainer_id = (
                        int(hashlib.md5(trainer_uuid.encode()).hexdigest()[:8], 16)
                        % 2147483647
                    )
                else:
                    trainer_id = (
                        int(hashlib.md5(trainer_name.encode()).hexdigest()[:8], 16)
                        % 2147483647
                    )

                if trainer_id not in trainers:
                    trainers[trainer_id] = {
                        "id": trainer_id,
                        "name": trainer_name,
                        "raw_json": {"source": "hrnz_scraper", "uuid": trainer_uuid},
                    }

        return {
            "horses": list(horses.values()),
            "drivers": list(drivers.values()),
            "trainers": list(trainers.values()),
        }
