"""
MSport Nigeria scraper.

API endpoints and required headers discovered via Playwright network interception.
Key headers: operid=2, clientid=WEB, apilevel=2, platform=WEB
"""
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.msport.com/api/ng/facts-center/query/frontend"
LIVE_ENDPOINT = f"{BASE_URL}/live-matches"

SPORT_IDS = {
    'football':   'sr:sport:1',
    'basketball': 'sr:sport:2',
    'tennis':     'sr:sport:5',
}

# Market IDs we care about for arb
ARBI_MARKETS = {
    'football':   {1: '1X2', 10: 'Double Chance', 11: 'Asian Handicap', 18: 'Over/Under 2.5', 26: 'Draw No Bet', 29: 'BTTS', 47: 'Correct Score'},
    'basketball': {219: 'Home/Away'},
    'tennis':     {186: 'Home/Away'},
}

# MSport outcome ID → name for football markets
OUTCOME_MAP = {
    '1': 'Home', '2': 'Draw', '3': 'Away',   # 1X2
    '9': '1X', '10': '12', '11': 'X2',       # Double Chance
    '12': 'Over', '13': 'Under',              # O/U
    '60': 'Home', '61': 'Away',              # Draw No Bet
    '74': 'Yes', '76': 'No',                  # BTTS
    '714': 'Home', '715': 'Away',            # Asian Handicap
}

# Per-market outcome ID → label (overrides OUTCOME_MAP)
MARKET_OUTCOME_MAP = {
    219: {'4': 'Home', '5': 'Away'},  # Basketball moneyline
    186: {'4': 'Home', '5': 'Away'},  # Tennis winner
}


def _parse_ah_line(specifier: str):
    """Extract and normalise AH line from a hcp= specifier string.
    Returns '+0.5', '-0.5', '0' etc. or None for quarter-ball lines."""
    for key in ('hcp=', 'handicap='):
        if key in specifier:
            try:
                raw = specifier.split(key)[1].split('&')[0].strip()
                val = float(raw)
                if abs(val * 4) % 2 != 0:   # reject quarter-ball (0.25, 0.75, ...)
                    return None
                return f'+{val:g}' if val > 0 else f'{val:g}'
            except (ValueError, IndexError):
                return None
    return None


class MSportScraper(BaseScraper):

    def __init__(self):
        super().__init__('MSport')
        # Replace session headers with exact set that works
        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.msport.com/ng/web',
            'clientid': 'WEB',
            'operid':   '2',
            'apilevel': '2',
            'platform': 'WEB',
        }

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

        events: List[Event] = []
        page = 1

        while True:
            try:
                raw_events, has_more = self._fetch_page(sport_id, page)
                for raw in raw_events:
                    if raw.get('betStop') or raw.get('active') == 0:
                        continue
                    events.extend(self._parse(raw, sport))
                if not has_more or page >= 10:   # cap at 1000 events
                    break
                page += 1
            except Exception as ex:
                logger.error(f"[MSport] {sport} page {page} error: {ex}")
                break

        return events

    def get_live_events(self, sport: str) -> List[Event]:
        sport_id = SPORT_IDS.get(sport)
        if not sport_id:
            return []
        events: List[Event] = []
        try:
            resp = self.session.get(
                LIVE_ENDPOINT,
                params={'sportId': sport_id, 'pageSize': 200, 'pageNum': 1},
                timeout=15,
            )
            resp.raise_for_status()
            for raw in resp.json().get('data', {}).get('events', []):
                events.extend(self._parse(raw, sport, live=True))
        except Exception as ex:
            logger.error(f'[MSport] live {sport} fetch error: {ex}')
        return events

    def _fetch_page(self, sport_id: str, page: int):
        url = f"{BASE_URL}/all-matches/next-7-days"
        params = {
            'sportId':  sport_id,
            'pageSize': 100,
            'pageNum':  page,
        }
        resp = self.session.get(url, params=params, timeout=15)
        resp.raise_for_status()
        data = resp.json().get('data', {})
        raw_events = data.get('events', [])
        # has_more: if we got a full page, there might be more
        has_more = len(raw_events) == 100
        return raw_events, has_more

    def _parse(self, raw: dict, sport: str, live: bool = False) -> List[Event]:
        home = (raw.get('homeTeam') or '').strip()
        away = (raw.get('awayTeam') or '').strip()
        if not home or not away:
            return []

        league   = (raw.get('tournament') or '').strip()
        event_id = raw.get('eventId', '')
        sport_s  = raw.get('sport') or 'Soccer'
        category = (raw.get('category') or '').replace(' ', '_')
        tourney  = league.replace(' ', '_')
        home_s   = home.replace(' ', '_')
        away_s   = away.replace(' ', '_')
        if event_id:
            if live:
                event_url = (
                    f'https://www.msport.com/ng/live/{sport_s}'
                    f'/{category}_{tourney}'
                    f'/{home_s}_vs_{away_s}'
                    f'/{event_id}'
                )
            elif category and tourney:
                event_url = (
                    f'https://www.msport.com/ng/web/sports/{sport_s}'
                    f'/{category}_{tourney}'
                    f'/{home_s}_vs_{away_s}'
                    f'/{event_id}'
                )
            else:
                event_url = None
        else:
            event_url = None

        ts = raw.get('startTime') or raw.get('estimateStartTime')
        try:
            starts_at = datetime.utcfromtimestamp(ts / 1000) if ts else None
        except Exception:
            starts_at = None

        arb_market_ids = ARBI_MARKETS.get(sport, {})
        result: List[Event] = []

        for market in raw.get('markets', []):
            market_id = market.get('id')
            if market_id not in arb_market_ids:
                continue

            market_name = arb_market_ids[market_id]
            mkt_oc_map = MARKET_OUTCOME_MAP.get(market_id)
            ou_line = ''
            ah_line = ''

            if market_id == 47:
                pass  # Correct Score — no line detection needed

            elif market_id == 18:
                # Detect which line this market covers from the first active outcome description
                first_desc = next(
                    (o.get('description', '') for o in market.get('outcomes', []) if o.get('isActive', 1)),
                    ''
                )
                for _lv in ('0.5', '1.5', '2.5', '3.5', '4.5'):
                    if _lv in first_desc:
                        ou_line = _lv
                        market_name = f'Over/Under {_lv}'
                        break
                if not ou_line:
                    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 not o.get('isActive', 1):
                    continue
                oid = str(o.get('id', ''))
                desc = o.get('description', '')

                if market_id == 47:
                    # Correct Score — description IS the score string
                    name = normalize_cs_score(desc)
                    if not name:
                        continue
                elif market_id == 18:
                    if ou_line not in desc:
                        continue
                    name = 'Over' if desc.startswith('Over') else 'Under'
                elif market_id == 11:
                    # AH: use outcome ID map (714=Home, 715=Away)
                    name = OUTCOME_MAP.get(oid)
                    if name is None:
                        continue
                elif mkt_oc_map is not None:
                    name = mkt_oc_map.get(oid)
                    if name is None:
                        continue
                else:
                    name = OUTCOME_MAP.get(oid, desc)
                try:
                    odds = float(o.get('odds', 0))
                except (TypeError, ValueError):
                    continue
                if odds > 1.0:
                    outcomes.append(Outcome(name=name, odds=odds, bookmaker='MSport', event_url=event_url))

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

        return result
