"""
SportyBet Nigeria scraper.

Endpoint discovered via Playwright network interception on sportybet.com/ng/sport/football
Requires _t (unix ms timestamp) query param — the API is open once the timestamp is added.
"""
import time
import logging
from datetime import datetime
from typing import List, Optional

from scrapers.base import BaseScraper, normalize_cs_score
from core.models import Event, Outcome

logger = logging.getLogger(__name__)

BASE_URL      = "https://www.sportybet.com/api/ng/factsCenter/pcUpcomingEvents"
LIVE_BASE_URL = "https://www.sportybet.com/api/ng/factsCenter/liveOrPrematchEvents"

# Market IDs discovered from browser network tab
SPORT_CONFIG = {
    'football': {
        'sport_id':   'sr%3Asport%3A1',
        'market_ids': '1%2C60%2C18%2C10%2C29%2C11%2C26%2C68%2C90%2C47',
        'slug':       'football',
    },
    'basketball': {
        'sport_id':   'sr%3Asport%3A2',
        'market_ids': '219%2C18%2C223%2C1%2C14',
        'slug':       'basketball',
    },
    'tennis': {
        'sport_id':   'sr%3Asport%3A5',
        'market_ids': '186%2C189%2C202%2C188%2C18',
        'slug':       'tennis',
    },
}

# Market ID → display name
MARKET_NAMES = {
    '1':   '1X2',
    '10':  'Double Chance',
    '18':  'Over/Under 2.5',
    '26':  'Draw No Bet',
    '29':  'BTTS',
    '47':  'Correct Score',
    '60':  'HT 1X2',
    '219': 'Home/Away',
    '186': 'Home/Away',
}

# Outcome ID → display name (SportyBet uses numeric IDs for 1X2)
OUTCOME_MAP = {
    '1': 'Home', '2': 'Draw', '3': 'Away',   # 1X2
    '4': 'Over', '5': 'Under',                # Over/Under (FT)
    '12': 'Over', '13': 'Under',              # HT/2H Over/Under
    '6': 'Home', '7': 'Away',                 # Moneyline (basketball/tennis)
    '9': '1X', '10': '12', '11': 'X2',        # Double Chance
    '60': 'Home', '61': 'Away',               # Draw No Bet
    '74': 'Yes', '76': 'No',                  # BTTS
    '714': 'Home', '715': 'Away',             # Asian Handicap
}


def _parse_ah_line(specifier: str):
    """Extract and normalise Asian Handicap line from a Sportradar hcp= specifier.

    Returns a string like '-0.5', '+0.5', '0' etc., or None if the line is a
    quarter-ball (0.25 / 0.75 steps) that won't match across bookmakers.
    """
    for key in ('hcp=', 'handicap='):
        if key in specifier:
            try:
                raw = specifier.split(key)[1].split('&')[0].strip()
                val = float(raw)
                # Reject quarter-ball lines (e.g. 0.25, 0.75, -1.25, -1.75)
                if abs(val * 4) % 2 != 0:
                    return None
                if val > 0:
                    return f'+{val:g}'
                return f'{val:g}'
            except (ValueError, IndexError):
                return None
    return None


class SportyBetScraper(BaseScraper):

    def __init__(self):
        super().__init__('SportyBet')
        # Replace headers entirely — Accept-Language causes 422 on this API
        self.session.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'Accept': 'application/json, text/plain, */*',
            'Referer': 'https://www.sportybet.com/ng/sport/football',
            'Origin':  'https://www.sportybet.com',
        }

    def get_events(self, sport: str) -> List[Event]:
        cfg = SPORT_CONFIG.get(sport)
        if not cfg:
            return []

        events: List[Event] = []
        try:
            data = self._fetch(cfg['sport_id'], cfg['market_ids'])
            sport_slug = cfg['slug']
            tournaments = data.get('tournaments', [])
            for tournament in tournaments:
                league = tournament.get('name', '')
                for raw in tournament.get('events', []):
                    if raw.get('betStop') or raw.get('active') == 0:
                        continue
                    parsed = self._parse(raw, sport, league, sport_slug)
                    events.extend(parsed)
        except Exception as ex:
            logger.error(f"[SportyBet] {sport} fetch error: {ex}")

        return events

    def get_live_events(self, sport: str) -> List[Event]:
        cfg = SPORT_CONFIG.get(sport)
        if not cfg:
            return []

        events: List[Event] = []
        try:
            data = self._fetch_live(cfg['sport_id'], cfg['market_ids'])
            sport_slug = cfg['slug']
            for tournament in data:
                league = tournament.get('name', '')
                for raw in tournament.get('events', []):
                    if raw.get('status') != 1:
                        continue  # skip prematch events in the combined feed
                    parsed = self._parse(raw, sport, league, sport_slug, live=True)
                    events.extend(parsed)
        except Exception as ex:
            logger.error(f"[SportyBet] live {sport} fetch error: {ex}")

        return events

    def _fetch(self, sport_id: str, market_ids: str) -> dict:
        # Build URL manually — passing via params dict changes encoding and causes 422
        ts = int(time.time() * 1000)
        url = (
            f"{BASE_URL}"
            f"?sportId={sport_id}"
            f"&marketId={market_ids}"
            f"&pageSize=100&pageNum=1&option=1&_t={ts}"
        )
        resp = self.session.get(url, timeout=15)
        resp.raise_for_status()
        return resp.json().get('data', {})

    def _fetch_live(self, sport_id: str, market_ids: str) -> list:
        ts = int(time.time() * 1000)
        url = (
            f"{LIVE_BASE_URL}"
            f"?sportId={sport_id}"
            f"&marketId={market_ids}"
            f"&pageSize=100&pageNum=1&option=1&_t={ts}"
        )
        resp = self.session.get(url, timeout=15)
        resp.raise_for_status()
        # liveOrPrematchEvents returns a list directly under 'data'
        return resp.json().get('data', [])

    def _parse(self, raw: dict, sport: str, league: str = '', sport_slug: str = '', live: bool = False) -> List[Event]:
        """Return one Event per market in the raw event."""
        home = raw.get('homeTeamName', '').strip()
        away = raw.get('awayTeamName', '').strip()
        if not home or not away:
            return []

        ts = raw.get('estimateStartTime', 0)
        starts_at = datetime.utcfromtimestamp(ts / 1000) if ts else None

        event_id_raw = raw.get('eventId', '')
        if sport_slug and event_id_raw:
            if live:
                event_url = f'https://www.sportybet.com/ng/sport/live/{sport_slug}/{event_id_raw}'
            else:
                sport_data = raw.get('sport', {})
                category   = sport_data.get('category', {}).get('name', '').replace(' ', '_')
                tournament = (sport_data.get('category', {}).get('tournament', {})
                              .get('name', '').replace(' ', '_'))
                home_slug  = home.replace(' ', '_')
                away_slug  = away.replace(' ', '_')
                if category and tournament:
                    event_url = (f'https://www.sportybet.com/ng/sport/{sport_slug}'
                                 f'/{category}/{tournament}/{home_slug}_vs_{away_slug}/{event_id_raw}')
                else:
                    event_url = f'https://www.sportybet.com/ng/sport/{sport_slug}/{event_id_raw}'
        else:
            event_url = None

        result: List[Event] = []
        for market in raw.get('markets', []):
            market_id = str(market.get('id', ''))
            market_name = MARKET_NAMES.get(market_id, market.get('name', market_id))

            # For Over/Under market, accept 0.5 / 1.5 / 2.5 / 3.5 / 4.5 lines
            ou_line = ''
            ah_line = ''
            if market_id == '18':
                specifier = market.get('specifier', '')
                for _lv in ('0.5', '1.5', '2.5', '3.5', '4.5'):
                    if f'total={_lv}' in specifier:
                        ou_line = _lv
                        market_name = f'Over/Under {_lv}'
                        break
                else:
                    continue

            elif market_id == '68':
                specifier = market.get('specifier', '')
                for _lv in ('0.5', '1.5'):
                    if f'total={_lv}' in specifier:
                        ou_line = _lv
                        market_name = f'HT Over/Under {_lv}'
                        break
                else:
                    continue

            elif market_id == '90':
                specifier = market.get('specifier', '')
                for _lv in ('0.5', '1.5', '2.5'):
                    if f'total={_lv}' in specifier:
                        ou_line = _lv
                        market_name = f'2H Over/Under {_lv}'
                        break
                else:
                    continue

            elif market_id == '11':
                # Asian Handicap — line from hcp= specifier
                specifier = market.get('specifier', '')
                ah_line = _parse_ah_line(specifier)
                if ah_line is None:
                    continue
                market_name = f'Asian Handicap {ah_line}'

            # Skip market if suspended/locked at the market level
            if (market.get('betStop')
                    or market.get('active') == 0
                    or market.get('isActive') == 0
                    or str(market.get('status', '')).lower() in ('suspended', 'closed')):
                continue

            outcomes = []
            for o in market.get('outcomes', []):
                if o.get('isActive', 1) != 1:
                    continue  # skip suspended/locked outcomes
                oid = str(o.get('id', ''))
                if market_id == '47':
                    # Correct Score — outcome name is the score string in desc
                    name = normalize_cs_score(o.get('desc', ''))
                    if not name:
                        continue
                elif market_id in ('219', '186'):
                    # Basketball/tennis H/A — desc has team name
                    name = o.get('desc', oid)
                else:
                    name = OUTCOME_MAP.get(oid, o.get('desc', oid))
                try:
                    odds = float(o.get('odds', 0))
                except (TypeError, ValueError):
                    continue
                if odds > 1.0:
                    outcomes.append(Outcome(name=name, odds=odds, bookmaker='SportyBet', event_url=event_url))

            if len(outcomes) >= 2:
                _line_sfx = ou_line or ah_line or ''
                result.append(Event(
                    event_id=f"sb_{raw.get('eventId', '')}_{market_id}{'_' + _line_sfx if _line_sfx else ''}",
                    bookmaker='SportyBet',
                    sport=sport,
                    home_team=home,
                    away_team=away,
                    market=market_name,
                    outcomes=outcomes,
                    starts_at=starts_at,
                    league=league,
                ))

        return result
