"""
NairaBet Nigeria scraper.

Platform: Altenar (sb2frontend-altenar2.biahosted.com)
Integration: nairabet

Events + odds endpoint:
  GET https://sb2frontend-altenar2.biahosted.com/api/Widget/GetEvents
  Params: culture=en-GB&timezoneOffset=300&integration=nairabet&deviceType=1
          &numFormat=en-GB&countryCode=NG&sportId={sport_id}&count=2000

Response structure (flat arrays linked by IDs):
  events[]  → id, name ("Home vs. Away"), startDate, marketIds, competitorIds
  markets[] → id, typeId, oddIds
  odds[]    → id, typeId, name, price, oddStatus, competitorId (optional)

Market typeIds:
  Football:
    1   → 1X2       (odd.typeId 1=Home, 2=Draw, 3=Away)
    18  → Total     (odd.name contains "Over 2.5" / "Under 2.5")
  Basketball:
    219 → Winner    (match odd.competitorId to event.competitorIds[0/1])
  Tennis:
    186 → Winner    (match odd.competitorId to event.competitorIds[0/1])

Sport IDs: football=66, basketball=67, tennis=68
"""
import logging
from datetime import datetime
from typing import List, Dict

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

logger = logging.getLogger(__name__)

BASE_URL      = 'https://sb2frontend-altenar2.biahosted.com/api/Widget/GetEvents'
LIVE_BASE_URL = 'https://sb2frontend-altenar2.biahosted.com/api/Widget/GetLiveEvents'
COMMON_PARAMS = (
    'culture=en-GB&timezoneOffset=300&integration=nairabet'
    '&deviceType=1&numFormat=en-GB&countryCode=NG'
)

SPORT_IDS = {
    'football':   66,
    'basketball': 67,
    'tennis':     68,
}

# {sport: {typeId: market_name}}
MARKETS = {
    'football':   {1: '1X2', 10: 'Double Chance', 11: 'Asian Handicap', 18: 'Over/Under 2.5', 26: 'Draw No Bet', 29: 'BTTS'},
    'basketball': {219: 'Home/Away'},
    'tennis':     {186: 'Home/Away'},
}

# Altenar odd typeId → outcome label for 1x2
ODD_TYPE_MAP = {1: 'Home', 2: 'Draw', 3: 'Away'}

# DNB odd names on Altenar: "W1" = home wins, "W2" = away wins
_DNB_NAME_MAP = {'W1': 'Home', 'W2': 'Away', '1': 'Home', '2': 'Away'}


class NairaBetScraper(BaseScraper):

    def __init__(self):
        super().__init__('NairaBet')
        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, */*',
            'Origin': 'https://www.nairabet.com',
            'Referer': 'https://www.nairabet.com/',
        }

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

        events: List[Event] = []
        try:
            raw = self._fetch(sport_id)
            events = self._parse_all(raw, sport)
        except Exception as ex:
            logger.error(f'[NairaBet] {sport} fetch error: {ex}')

        return events

    def get_live_events(self, sport: str) -> List[Event]:
        sport_id = SPORT_IDS.get(sport)
        if not sport_id:
            return []
        try:
            url = f'{LIVE_BASE_URL}?{COMMON_PARAMS}&sportId={sport_id}'
            r = self.session.get(url, timeout=15)
            r.raise_for_status()
            return self._parse_all(r.json(), sport)
        except Exception as ex:
            logger.error(f'[NairaBet] live {sport} fetch error: {ex}')
            return []

    def _fetch(self, sport_id: int) -> dict:
        url = f'{BASE_URL}?{COMMON_PARAMS}&sportId={sport_id}&count=2000'
        r = self.session.get(url, timeout=20)
        r.raise_for_status()
        return r.json()

    def _parse_all(self, raw: dict, sport: str) -> List[Event]:
        # Build lookup dicts
        markets_by_id: Dict[int, dict] = {m['id']: m for m in raw.get('markets', [])}
        odds_by_id: Dict[int, dict]    = {o['id']: o for o in raw.get('odds', [])}
        champs_by_id: Dict[int, str]   = {c['id']: c.get('name', '') for c in raw.get('champs', [])}

        sport_markets = MARKETS.get(sport, {})
        result: List[Event] = []

        for ev in raw.get('events', []):
            ev_id    = ev.get('id', '')
            name     = (ev.get('name') or '').strip()
            comp_ids = ev.get('competitorIds', [])
            league   = champs_by_id.get(ev.get('champId', ''), '')

            # Split "Home vs. Away"
            if ' vs. ' in name:
                home, away = [t.strip() for t in name.split(' vs. ', 1)]
            elif ' vs ' in name:
                home, away = [t.strip() for t in name.split(' vs ', 1)]
            else:
                continue

            if not home or not away:
                continue

            date_str = ev.get('startDate', '')
            try:
                starts_at = datetime.fromisoformat(date_str.replace('Z', '+00:00')).replace(tzinfo=None) if date_str else None
            except Exception:
                starts_at = None

            for mkt_id in ev.get('marketIds', []):
                market = markets_by_id.get(mkt_id)
                if not market:
                    continue

                type_id = market.get('typeId')
                if type_id not in sport_markets:
                    continue

                market_name = sport_markets[type_id]

                # O/U: emit a separate Event per line (1.5, 2.5, 3.5, 4.5)
                if type_id == 18:
                    for lv in ('0.5', '1.5', '2.5', '3.5', '4.5'):
                        ou_outcomes = self._parse_ou_line(market, odds_by_id, lv)
                        if len(ou_outcomes) == 2:
                            result.append(Event(
                                event_id=f'nb_{ev_id}_{mkt_id}_{lv}',
                                bookmaker='NairaBet',
                                sport=sport,
                                home_team=home,
                                away_team=away,
                                market=f'Over/Under {lv}',
                                outcomes=ou_outcomes,
                                starts_at=starts_at,
                                league=league,
                            ))
                    continue

                # AH: emit a separate Event per line, extracted from odd names
                if type_id == 11:
                    for ah_outcomes, ah_line in self._parse_ah_lines(market, odds_by_id):
                        result.append(Event(
                            event_id=f'nb_{ev_id}_{mkt_id}_{ah_line}',
                            bookmaker='NairaBet',
                            sport=sport,
                            home_team=home,
                            away_team=away,
                            market=f'Asian Handicap {ah_line}',
                            outcomes=ah_outcomes,
                            starts_at=starts_at,
                            league=league,
                        ))
                    continue

                outcomes = self._parse_outcomes(market, odds_by_id, type_id, comp_ids, sport)

                if not outcomes:
                    continue

                # Expect exactly 3 outcomes for 1X2/DC, 2 for all others
                expected = 3 if type_id in (1, 10) else 2
                # AH handled via _parse_ah_lines above — should not reach here
                if type_id == 11:
                    continue
                if len(outcomes) != expected:
                    continue

                result.append(Event(
                    event_id=f'nb_{ev_id}_{mkt_id}',
                    bookmaker='NairaBet',
                    sport=sport,
                    home_team=home,
                    away_team=away,
                    market=market_name,
                    outcomes=outcomes,
                    starts_at=starts_at,
                    league=league,
                ))

        return result

    def _parse_outcomes(self, market: dict, odds_by_id: Dict, type_id: int, comp_ids: list, sport: str) -> List[Outcome]:
        outcomes = []

        for oid in market.get('oddIds', []):
            odd = odds_by_id.get(oid)
            if not odd:
                continue
            if odd.get('oddStatus', 0) != 0:  # 0 = active
                continue

            price = odd.get('price', 0)
            try:
                price = float(price)
            except (TypeError, ValueError):
                continue
            if price <= 1.0:
                continue

            if type_id == 1:
                # 1X2: use odd.typeId (1=Home, 2=Draw, 3=Away)
                label = ODD_TYPE_MAP.get(odd.get('typeId'))
                if label is None:
                    continue

            elif type_id == 10:
                # Double Chance: odd.typeId 9=1X, 10=12, 11=X2 (confirmed from API)
                dc_map = {9: '1X', 10: '12', 11: 'X2'}
                label = dc_map.get(odd.get('typeId'))
                if label is None:
                    continue

            elif type_id == 18:
                # Total Goals: odd.name is "Over 2.5" or "Under 2.5"
                odd_name = odd.get('name', '')
                if '2.5' not in odd_name:
                    continue
                label = 'Over' if odd_name.startswith('Over') else 'Under'

            elif type_id == 26:
                # Draw No Bet: odd.name "W1"=Home or "W2"=Away (Altenar convention)
                odd_name = odd.get('name', '').strip()
                label = _DNB_NAME_MAP.get(odd_name)
                if label is None:
                    continue

            elif type_id == 29:
                # BTTS: odd.name is "GG" (Yes) or "NG" (No)
                odd_name = odd.get('name', '').strip().upper()
                if odd_name == 'GG':
                    label = 'Yes'
                elif odd_name == 'NG':
                    label = 'No'
                else:
                    continue

            else:
                # Winner markets (basketball 219, tennis 186):
                # use competitorId position in event.competitorIds
                cid = odd.get('competitorId')
                if cid is None or not comp_ids:
                    continue
                if cid == comp_ids[0]:
                    label = 'Home'
                elif len(comp_ids) > 1 and cid == comp_ids[1]:
                    label = 'Away'
                else:
                    continue

            outcomes.append(Outcome(name=label, odds=price, bookmaker='NairaBet'))

        return outcomes

    def _parse_ah_lines(self, market: dict, odds_by_id: dict):
        """Yield (outcomes, normalised_line) for each AH line in this market.

        Altenar AH odd names are e.g. 'Home (-0.5)', 'Away (+0.5)'.
        We extract the line from the first home outcome and emit one Event per line.
        """
        import re
        # Group odds by their handicap line (extracted from name)
        by_line: dict = {}
        for oid in market.get('oddIds', []):
            odd = odds_by_id.get(oid)
            if not odd or odd.get('oddStatus', 0) != 0:
                continue
            try:
                price = float(odd.get('price', 0))
            except (TypeError, ValueError):
                continue
            if price <= 1.0:
                continue
            name = odd.get('name', '').strip()
            # Name format: "Home (-0.5)" or "Away (+0.5)" — extract handicap in parens
            m = re.search(r'\(([+-]?\d+(?:\.\d+)?)\)', name)
            if not m:
                continue
            try:
                val = float(m.group(1))
            except ValueError:
                continue
            # Reject quarter-ball lines
            if abs(val * 4) % 2 != 0:
                continue
            line_str = f'+{val:g}' if val > 0 else f'{val:g}'
            label = 'Home' if name.lower().startswith('home') else 'Away'
            by_line.setdefault(line_str, []).append(
                Outcome(name=label, odds=price, bookmaker='NairaBet')
            )
        for line_str, outcomes in by_line.items():
            if len(outcomes) == 2:
                yield outcomes, line_str

    def _parse_ou_line(self, market: dict, odds_by_id: dict, line: str) -> List[Outcome]:
        """Extract Over/Under outcomes for a specific line (e.g. '1.5')."""
        outcomes = []
        for oid in market.get('oddIds', []):
            odd = odds_by_id.get(oid)
            if not odd or odd.get('oddStatus', 0) != 0:
                continue
            try:
                price = float(odd.get('price', 0))
            except (TypeError, ValueError):
                continue
            if price <= 1.0:
                continue
            odd_name = odd.get('name', '')
            if line not in odd_name:
                continue
            label = 'Over' if odd_name.startswith('Over') else 'Under'
            outcomes.append(Outcome(name=label, odds=price, bookmaker='NairaBet'))
        return outcomes
