"""
Standalone Flask server for the England Virtual Football League predictor.
Run separately from the main arb_bot:

    python web/virtual_app.py

Serves on port 5002. Every 2 minutes it fetches fixtures, calls Claude for
predictions, then pushes the full payload to Betsnipper. The /api/virtual/england
endpoint serves from the in-memory state (no WS call per request).
"""
import json
import logging
import os
import re
import sys
import time as _time
from datetime import datetime, timezone

import requests as _requests
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))

from apscheduler.schedulers.background import BackgroundScheduler
from flask import Flask, jsonify, request

import config
from scrapers.virtustec_england import get_england_data

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
    datefmt='%H:%M:%S',
)
logger = logging.getLogger(__name__)

VIRTUAL_PORT             = 5000
VIRTUAL_REFRESH_INTERVAL = 120  # seconds

app   = Flask(__name__)
application = app
_state = {'payload': None}


# ── Prediction logic ──────────────────────────────────────────────────────────

def _compute_history_stats(history: list) -> dict:
    if not history:
        return {}
    total = len(history)
    over  = sum(1 for m in history if m.get('ou25') == 'O')
    under = sum(1 for m in history if m.get('ou25') == 'U')
    return {
        'total':      total,
        'over25_pct': round(over / total * 100) if total else 0,
        'under25_pct': round(under / total * 100) if total else 0,
    }


def _build_team_history(history: list) -> dict:
    by_team: dict = {}
    for m in history:
        home   = m['home']
        away   = m['away']
        result = m.get('result', '?')
        by_team.setdefault(home, []).append({
            'opponent': away, 'venue': 'H', 'result': result,
            'home_score': m.get('home_score'), 'away_score': m.get('away_score'),
            'ou25': m.get('ou25'),
        })
        away_result = 'L' if result == 'H' else ('W' if result == 'A' else 'D')
        by_team.setdefault(away, []).append({
            'opponent': home, 'venue': 'A', 'result': away_result,
            'home_score': m.get('away_score'), 'away_score': m.get('home_score'),
            'ou25': m.get('ou25'),
        })
    return by_team


def _team_ou_stats(form: list) -> dict:
    total = sum(1 for m in form if m.get('ou25') is not None)
    over  = sum(1 for m in form if m.get('ou25') == 'O')
    return {'over': over, 'total': total}


def _build_prompt(home, away, h_stars, a_stars, odds1, odds_x, odds2,
                  odds_over25, odds_under25,
                  home_form, away_form, home_ou, away_ou, hist_stats,
                  total_xg, home_xg_h, away_xg_a, h2h_over, h2h_total) -> str:
    def fmt(form):
        if not form:
            return 'No recent data'
        return ', '.join(
            f"{m['venue']}:{m['result']}({m.get('ou25', '?')})" for m in form
        )

    league_info = (
        f"League O/U 2.5 split (last {hist_stats['total']} matches): "
        f"{hist_stats['over25_pct']}% Over, {hist_stats['under25_pct']}% Under"
        if hist_stats.get('total', 0) > 0 else 'League history not available'
    )

    ou_odds_line = ''
    if odds_over25 and odds_under25:
        ou_odds_line = f"O/U 2.5 odds: Over={odds_over25} / Under={odds_under25}\n"

    xg_line = ''
    if total_xg is not None:
        xg_line = (
            f"Simulation engine xG: {home}={home_xg_h} (home) / {away}={away_xg_a} (away) → "
            f"Total expected goals = {total_xg} ({'OVER' if total_xg > 2.5 else 'UNDER'} 2.5)\n"
        )

    h2h_line = ''
    if h2h_total > 0:
        h2h_line = f"Last {h2h_total} H2H meetings: {h2h_over} went Over 2.5 ({round(h2h_over/h2h_total*100)}%)\n"

    return (
        f"You are a sports analyst predicting outcomes for SportyBet England Virtual Football League.\n\n"
        f"Match: {home} (★{h_stars}) vs {away} (★{a_stars})\n"
        f"1X2 odds: Home={odds1} / Draw={odds_x} / Away={odds2}\n"
        f"{ou_odds_line}"
        f"{xg_line}"
        f"{h2h_line}"
        f"\n{home} recent form (venue:result(O/U)): {fmt(home_form)}\n"
        f"{away} recent form (venue:result(O/U)): {fmt(away_form)}\n\n"
        f"Home team O/U 2.5 in last {home_ou['total']} matches: {home_ou['over']} Over\n"
        f"Away team O/U 2.5 in last {away_ou['total']} matches: {away_ou['over']} Over\n"
        f"{league_info}\n\n"
        f"Note: This is a virtual football simulation. Star ratings (0-5) and xG values come "
        f"directly from the simulation engine — treat them as ground truth, not estimates. "
        f"Total xG is the single strongest predictor.\n\n"
        f'Respond with ONLY this JSON (no other text):\n'
        f'{{"prediction":"Over"|"Under","confidence":"High"|"Medium"|"Low","reasoning":"max 25 words"}}'
    )


def _parse_prediction(text: str, home_ou: dict, away_ou: dict, hist_stats: dict) -> dict:
    m = re.search(r'\{[^}]+\}', text)
    if m:
        try:
            data = json.loads(m.group())
            if 'prediction' in data:
                return {
                    'prediction': data['prediction'],
                    'confidence': data.get('confidence', 'Medium'),
                    'reasoning':  data.get('reasoning', ''),
                    'source':     'ai',
                }
        except (json.JSONDecodeError, ValueError):
            pass
    over_rate = hist_stats.get('over25_pct', 50)
    return {
        'prediction': 'Over' if over_rate >= 50 else 'Under',
        'confidence': 'Low',
        'reasoning':  'Based on league statistics only.',
        'source':     'fallback',
    }


def build_predictions(upcoming: list, history: list) -> dict:
    if not upcoming or not config.ANTHROPIC_API_KEY:
        return {}
    hist_stats   = _compute_history_stats(history)
    team_history = _build_team_history(history)
    predictions  = {}
    for match in upcoming:
        home      = match['home']
        away      = match['away']
        home_form = team_history.get(home, [])[:5]
        away_form = team_history.get(away, [])[:5]
        home_ou   = _team_ou_stats(home_form)
        away_ou   = _team_ou_stats(away_form)
        prompt    = _build_prompt(
            home, away,
            match.get('home_stars', 0), match.get('away_stars', 0),
            match.get('odds_1', 0), match.get('odds_x', 0), match.get('odds_2', 0),
            match.get('odds_over25'), match.get('odds_under25'),
            home_form, away_form, home_ou, away_ou, hist_stats,
            match.get('total_xg'), match.get('home_xg_h'), match.get('away_xg_a'),
            match.get('h2h_over', 0), match.get('h2h_total', 0),
        )
        try:
            resp = _requests.post(
                'https://api.anthropic.com/v1/messages',
                headers={
                    'x-api-key':         config.ANTHROPIC_API_KEY,
                    'anthropic-version': '2023-06-01',
                    'content-type':      'application/json',
                },
                json={
                    'model':      'claude-haiku-4-5-20251001',
                    'max_tokens': 300,
                    'messages':   [{'role': 'user', 'content': prompt}],
                },
                timeout=25,
            )
            resp.raise_for_status()
            text = resp.json().get('content', [{}])[0].get('text', '')
            pred = _parse_prediction(text, home_ou, away_ou, hist_stats)
        except Exception as e:
            logger.warning(f'Claude prediction failed for {home} vs {away}: {e}')
            pred = _parse_prediction('', home_ou, away_ou, hist_stats)
        key = f"{home}_vs_{away}_{match.get('block_id', '')}"
        predictions[key] = pred
    return predictions


# ── Push to Betsnipper ────────────────────────────────────────────────────────

def _push_to_betsnipper(payload: dict) -> None:
    url = getattr(config, 'BETSNIPPER_VIRTUAL_INGEST_URL', '')
    if not url:
        return
    for attempt in range(1, 4):
        try:
            resp = _requests.post(
                url,
                json=payload,
                headers={'X-Api-Key': config.BETSNIPPER_API_KEY},
                timeout=15,
            )
            if resp.status_code == 200:
                logger.info('[VirtualIngest] push OK')
                return
            else:
                logger.warning(f'[VirtualIngest] push failed: HTTP {resp.status_code} {resp.text[:120]}')
                return
        except Exception as e:
            logger.warning(f'[VirtualIngest] push error (attempt {attempt}/3): {e}')
            if attempt < 3:
                _time.sleep(5)


# ── Refresh cycle ─────────────────────────────────────────────────────────────

def refresh_and_push() -> None:
    logger.info('── Virtual refresh started ──')
    try:
        data      = get_england_data(history_rounds=3)
        upcoming  = data.get('upcoming', [])
        history   = data.get('history', [])
        secs_ahead = data.get('seconds_ahead', 0)
        if not upcoming:
            logger.info('── No qualifying upcoming block found (all too soon) ──')
            return
        preds     = build_predictions(upcoming, history)
        first     = upcoming[0] if upcoming else {}
        payload   = {
            'upcoming':     upcoming,
            'history':      history,
            'predictions':  preds,
            'league_id':    first.get('league_id'),
            'match_day':    first.get('match_day'),
            'seconds_ahead': secs_ahead,
            'fetched_at':   datetime.now(timezone.utc).isoformat(),
        }
        _state['payload'] = payload
        _push_to_betsnipper(payload)
        logger.info(
            f'── Virtual done: {len(upcoming)} upcoming, {len(preds)} predictions ──'
        )
    except Exception:
        logger.exception('Virtual refresh error')


# ── Routes ────────────────────────────────────────────────────────────────────

@app.route('/api/virtual/england')
def api_virtual_england():
    """Serve last cached data; trigger a fetch if state is empty."""
    payload = _state.get('payload')
    if payload is None:
        # First request before scheduler fires — fetch on-demand
        try:
            history  = int(request.args.get('history', 3))
            data = get_england_data(history_rounds=history)
            return jsonify({'status': 'ok', **data})
        except Exception as e:
            logger.exception('virtual england fetch error')
            return jsonify({'status': 'error', 'error': str(e)}), 500
    return jsonify({'status': 'ok', **payload})


@app.route('/health')
def health():
    return jsonify({'status': 'ok', 'last_refresh': _state['payload'] and _state['payload'].get('fetched_at')})


# ── Entry point ───────────────────────────────────────────────────────────────

if __name__ == '__main__':
    logger.info(f'Virtual predictor API running at http://localhost:{VIRTUAL_PORT}')
    logger.info('Running initial fetch + push...')
    refresh_and_push()

    scheduler = BackgroundScheduler(daemon=True)
    scheduler.add_job(refresh_and_push, 'interval', seconds=VIRTUAL_REFRESH_INTERVAL)
    scheduler.start()

    app.run(host='0.0.0.0', port=VIRTUAL_PORT, debug=True)
