from fastapi import APIRouter, Depends, HTTPException, Request, Body, Query
from pydantic import BaseModel, Field
from typing import List, Optional, Dict, Any
from datetime import datetime, timezone
import uuid
import re
import os
import json
import jwt
import gzip
import html
import urllib.request
import asyncio
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

from idg_bracket_engine import generate_single_elimination_bracket, advance_bracket_winner
from idg_server_pool import TournamentServerPool

router = APIRouter(prefix="/tournaments", tags=["Tournaments & Matches"])

def now_iso():
    return datetime.now(timezone.utc).isoformat()

security = HTTPBearer(auto_error=False)
JWT_SECRET = os.environ.get('JWT_SECRET', 'idg_forum_super_secret_key_2026')
WEBHOOK_SECRET_TOKEN = os.environ.get('TOURNAMENT_WEBHOOK_SECRET', 'ind_sec_2026_tournaments')

async def get_current_user(request: Request, creds: HTTPAuthorizationCredentials = Depends(security)):
    if not creds: return None
    try:
        payload = jwt.decode(creds.credentials, JWT_SECRET, algorithms=["HS256"])
        user_id = payload.get("sub")
        if not user_id: return None
        db = request.app.state.db
        user = await db.users.find_one({"id": user_id})
        return user
    except Exception:
        return None

async def require_auth(user = Depends(get_current_user)):
    if not user:
        raise HTTPException(status_code=401, detail="Trebuie să fii autentificat.")
    return user

async def require_admin(user = Depends(require_auth)):
    if user.get("role") not in ["admin", "root"]:
        raise HTTPException(status_code=403, detail="Acces interzis. Doar staff-ul poate efectua această acțiune.")
    return user

# DTOs
class TournamentCreate(BaseModel):
    title: str
    game: str = "cs16"  # cs16 or cs2
    prize_pool: str = "500 EUR + VIP Gold"
    max_teams: int = 16
    format: str = "Single Elimination (BO1)"
    start_date: str
    banner_url: Optional[str] = ""
    description: str
    rules: Optional[str] = ""
    stream_url: Optional[str] = ""

class MatchCreate(BaseModel):
    tournament_id: Optional[str] = None
    tournament_name: Optional[str] = "INDUNGI Cup"
    game: str = "cs16"
    team1_name: str
    team1_logo: Optional[str] = ""
    team1_tag: Optional[str] = ""
    team1_score: int = 0
    team2_name: str
    team2_logo: Optional[str] = ""
    team2_tag: Optional[str] = ""
    team2_score: int = 0
    status: str = "upcoming"  # live, upcoming, finished
    start_time: str
    stream_url: Optional[str] = ""
    map_name: Optional[str] = "de_dust2"
    format: str = "BO1"

class ReportScoreRequest(BaseModel):
    team1_score: int
    team2_score: int
    winning_team: Optional[str] = None  # "team1" or "team2"

class WebhookScoreReport(BaseModel):
    token: str
    match_id: str
    ct_score: int
    t_score: int
    map: Optional[str] = "de_dust2"
    ct_team: Optional[str] = "team1"

# ----------------- REAL-TIME ESPORTS SYNC ENGINE -----------------
async def sync_real_esports_matches(db) -> int:
    """Fetch live and upcoming real CS2 pro matches directly from official Liquipedia esports feed."""
    url = "https://liquipedia.net/counterstrike/api.php?action=parse&page=Liquipedia:Matches&format=json"
    headers = {
        "User-Agent": "INDUNGICommunityEsports/1.0 (contact: admin@indungi.ro; Romanian CS Gaming Community)",
        "Accept-Encoding": "gzip"
    }
    
    def _fetch():
        req = urllib.request.Request(url, headers=headers)
        with urllib.request.urlopen(req, timeout=12) as r:
            raw = r.read()
            content = gzip.decompress(raw).decode("utf-8")
            data = json.loads(content)
            return data.get("parse", {}).get("text", {}).get("*", "")

    try:
        html_raw = await asyncio.to_thread(_fetch)
    except Exception as e:
        print("Error fetching real Liquipedia matches:", e)
        return 0

    parts = html_raw.split("match-info-header-opponent")
    count = 0
    now_ts = datetime.now(timezone.utc).timestamp()
    
    for i in range(1, len(parts)):
        chunk = parts[i]
        names = re.findall(r'title="([^"]+)"', chunk)
        clean_names = [n for n in names if not any(x in n.lower() for x in ["liquipedia", "file:", "category:", "edit", "template:", "tier", "counter-strike"])]
        
        if len(clean_names) < 2:
            continue
            
        t1, t2 = clean_names[0], clean_names[1]
        score_matches = re.findall(r'class="scoreholder-score"[^>]*>(\d+)</span>', chunk)
        s1 = int(score_matches[0]) if len(score_matches) > 0 else 0
        s2 = int(score_matches[1]) if len(score_matches) > 1 else 0
        
        # Extract Tournament
        tourn_match = re.search(r'<span class="match-info-header-tournament"[^>]*><a[^>]*title="([^"]+)"', chunk)
        tourn_name = tourn_match.group(1) if tourn_match else "CS2 Pro Championship"
        
        # Timestamp
        time_match = re.search(r'data-timestamp="(\d+)"', chunk)
        match_ts = int(time_match.group(1)) if time_match else int(now_ts)
        start_time_iso = datetime.fromtimestamp(match_ts, timezone.utc).isoformat()
        
        # Determine status
        diff_sec = match_ts - now_ts
        if -10800 <= diff_sec <= 1800 and (s1 > 0 or s2 > 0 or diff_sec < 0):
            status = "live"
        elif diff_sec < -10800 or s1 >= 2 or s2 >= 2 or s1 >= 13 or s2 >= 13:
            status = "finished"
        else:
            status = "upcoming"
            
        slug = f"real-{re.sub(r'[^a-zA-Z0-9]', '', t1.lower())[:8]}-{re.sub(r'[^a-zA-Z0-9]', '', t2.lower())[:8]}-{match_ts}"
        
        doc = {
            "id": slug,
            "tournament_name": tourn_name,
            "game": "cs2",
            "team1_name": t1,
            "team1_tag": t1[:4].upper(),
            "team1_logo": f"https://api.dicebear.com/7.x/identicon/svg?seed={t1}",
            "team1_score": s1,
            "team2_name": t2,
            "team2_tag": t2[:4].upper(),
            "team2_logo": f"https://api.dicebear.com/7.x/identicon/svg?seed={t2}",
            "team2_score": s2,
            "status": status,
            "start_time": start_time_iso,
            "stream_url": "https://www.twitch.tv/eslcs",
            "map_name": "de_mirage",
            "format": "BO3",
            "source": "liquipedia",
            "updated_at": now_iso()
        }
        
        await db.idg_matches.update_one({"id": slug}, {"$set": doc}, upsert=True)
        count += 1
        
    return count

# ----------------- SEED INITIAL TOURNAMENTS -----------------
async def seed_tournaments_if_empty(db):
    cnt = await db.idg_tournaments.count_documents({})
    if cnt == 0:
        initial_tournaments = [
            {
                "id": "tourn-cs16-cup-1",
                "title": "Cupa Legendelor [CS 1.6] - Sezonul 1",
                "game": "cs16",
                "prize_pool": "300 EUR + VIP Gold",
                "max_teams": 8,
                "enrolled_teams": [
                    {"id": "clan_osl", "name": "OldSchool Legends", "tag": "OSL", "logo": "https://api.dicebear.com/7.x/identicon/svg?seed=OSL"},
                    {"id": "clan_tfr", "name": "TaskForce Romania", "tag": "TFR", "logo": "https://api.dicebear.com/7.x/identicon/svg?seed=TFR"},
                    {"id": "clan_ind", "name": "INDUNGI Elite Squad", "tag": "IND", "logo": "https://api.dicebear.com/7.x/identicon/svg?seed=IND"},
                    {"id": "clan_mix", "name": "Mix Masters", "tag": "MIX", "logo": "https://api.dicebear.com/7.x/identicon/svg?seed=MIX"}
                ],
                "format": "Single Elimination (BO1)",
                "status": "registration",
                "start_date": "2026-08-25T19:00:00Z",
                "banner_url": "https://images.unsplash.com/photo-1542751371-adc38448a05e?auto=format&fit=crop&w=1200&q=80",
                "description": "Campionat clasic 5v5 dedicat jucătorilor de legendă din CS 1.6! Meciuri automatizate pe serverul MIX.INDUNGI.PRO.",
                "rules": "Setări standard ESL/ReHLDS 1000 FPS. Fără alias-uri sau scripturi de silent-defuse.",
                "stream_url": "",
                "created_at": now_iso()
            },
            {
                "id": "tourn-cs2-cup-1",
                "title": "Cupa INDUNGI ROMANIA CS2 - Sezonul 1",
                "game": "cs2",
                "prize_pool": "1.000 EUR + VIP Diamond",
                "max_teams": 8,
                "enrolled_teams": [
                    {"id": "clan_head", "name": "Headshot Hunters", "tag": "HH", "logo": "https://api.dicebear.com/7.x/identicon/svg?seed=HH"},
                    {"id": "clan_vpr", "name": "Viper Gaming", "tag": "VPR", "logo": "https://api.dicebear.com/7.x/identicon/svg?seed=VPR"}
                ],
                "format": "Single Elimination (BO3)",
                "status": "registration",
                "start_date": "2026-09-01T18:00:00Z",
                "banner_url": "https://images.unsplash.com/photo-1511512578047-dfb367046420?auto=format&fit=crop&w=1200&q=80",
                "description": "Turneul oficial de Counter-Strike 2 al comunității INDUNGI! Meciuri MR12 pe servere dedicate 128-tick.",
                "rules": "Hărți Active Duty: Mirage, Inferno, Nuke, Dust2, Ancient, Anubis.",
                "stream_url": "https://www.twitch.tv/eslcs",
                "created_at": now_iso()
            }
        ]
        await db.idg_tournaments.insert_many(initial_tournaments)

# ----------------- ENDPOINTS -----------------

@router.get("")
async def list_tournaments(request: Request, game: Optional[str] = None, status: Optional[str] = None):
    db = request.app.state.db
    await seed_tournaments_if_empty(db)
    
    q: Dict[str, Any] = {}
    if game and game != "all": q["game"] = game
    if status and status != "all": q["status"] = status
    
    tournaments = await db.idg_tournaments.find(q, {"_id": 0}).sort("created_at", -1).to_list(50)
    return {"tournaments": tournaments}

@router.get("/matches")
async def list_matches(request: Request, status: Optional[str] = None, game: Optional[str] = None):
    db = request.app.state.db
    
    q: Dict[str, Any] = {}
    if status and status != "all": q["status"] = status
    if game and game != "all": q["game"] = game
    
    matches = await db.idg_matches.find(q, {"_id": 0}).sort("start_time", -1).to_list(60)
    if len(matches) == 0:
        await sync_real_esports_matches(db)
        matches = await db.idg_matches.find(q, {"_id": 0}).sort("start_time", -1).to_list(60)
        
    return {"matches": matches}

@router.post("/sync-esports")
async def sync_esports_matches_endpoint(request: Request, user = Depends(require_admin)):
    db = request.app.state.db
    count = await sync_real_esports_matches(db)
    return {"message": f"S-au sincronizat {count} meciuri reale oficiale CS2 în timp real.", "count": count}

@router.get("/{id}")
async def get_tournament(id: str, request: Request):
    db = request.app.state.db
    t = await db.idg_tournaments.find_one({"id": id}, {"_id": 0})
    if not t:
        raise HTTPException(status_code=404, detail="Turneul nu a fost găsit.")
        
    matches = await db.idg_matches.find({"tournament_id": id}, {"_id": 0}).sort("round", 1).to_list(100)
    return {"tournament": t, "matches": matches}

@router.get("/{id}/bracket")
async def get_tournament_bracket(id: str, request: Request):
    """Returns the visual bracket tree structure for the tournament."""
    db = request.app.state.db
    t = await db.idg_tournaments.find_one({"id": id}, {"_id": 0})
    if not t:
        raise HTTPException(status_code=404, detail="Turneul nu a fost găsit.")
        
    matches = await db.idg_matches.find({"tournament_id": id}, {"_id": 0}).sort([("round", 1), ("position", 1)]).to_list(100)
    
    # Group by rounds
    rounds = {}
    for m in matches:
        r_num = m.get("round", 1)
        if r_num not in rounds:
            rounds[r_num] = {
                "round": r_num,
                "round_name": m.get("round_name", f"Runda {r_num}"),
                "matches": []
            }
        rounds[r_num]["matches"].append(m)
        
    return {
        "tournament_id": id,
        "title": t.get("title"),
        "status": t.get("status"),
        "champion": t.get("champion"),
        "rounds": list(rounds.values())
    }

@router.post("/{id}/start")
async def start_tournament_and_generate_bracket(id: str, request: Request, user = Depends(require_admin)):
    """
    Starts the tournament: generates the Single Elimination bracket, assigns real servers with RCON,
    and sets tournament to in_progress.
    """
    db = request.app.state.db
    t = await db.idg_tournaments.find_one({"id": id})
    if not t:
        raise HTTPException(status_code=404, detail="Turneul nu a fost găsit.")
        
    enrolled = t.get("enrolled_teams", [])
    if len(enrolled) < 2:
        raise HTTPException(status_code=400, detail="Sunt necesare cel puțin 2 echipe înscrise pentru a porni turneul.")

    # 1. Clean existing matches for this tournament
    await db.idg_matches.delete_many({"tournament_id": id})

    # 2. Generate bracket tree
    bracket_matches = generate_single_elimination_bracket(id, enrolled, t.get("format", "BO1"))

    # 3. Server Pool instance
    server_pool = TournamentServerPool(db)
    game = t.get("game", "cs16")

    # 4. Prepare Round 1 matches and allocate server for active games
    for m in bracket_matches:
        m["tournament_name"] = t.get("title")
        m["game"] = game
        m["start_time"] = t.get("start_date")
        
        # If match is ready to play (both teams present, not a bye)
        if m.get("status") == "ready" and m.get("round") == 1:
            server_info = await server_pool.assign_server(
                tournament_id=id,
                match_id=m["id"],
                game=game,
                map_name=m.get("map_name", "de_dust2")
            )
            if server_info:
                m.update(server_info)

    # 5. Save all bracket matches to DB
    if bracket_matches:
        await db.idg_matches.insert_many(bracket_matches)

    # 6. Update tournament state
    await db.idg_tournaments.update_one(
        {"id": id},
        {"$set": {
            "status": "in_progress",
            "started_at": now_iso()
        }}
    )

    return {
        "message": f"Turneul '{t.get('title')}' a fost pornit cu succes! S-a generat bracket-ul automat.",
        "matches_count": len(bracket_matches)
    }

@router.post("/matches/{id}/report-score")
async def report_match_score(id: str, body: ReportScoreRequest, request: Request, user = Depends(require_auth)):
    """
    Submits match score (by captain or admin), advances winning team through the bracket,
    allocates next game server, and marks tournament champion if final.
    """
    db = request.app.state.db
    server_pool = TournamentServerPool(db)
    
    match = await db.idg_matches.find_one({"id": id})
    if not match:
        raise HTTPException(status_code=404, detail="Meciul nu a fost găsit.")

    # Determine winning team
    win_slot = body.winning_team
    if not win_slot:
        win_slot = "team1" if body.team1_score > body.team2_score else "team2"

    result = await advance_bracket_winner(
        db=db,
        match_id=id,
        winning_team_slot=win_slot,
        score1=body.team1_score,
        score2=body.team2_score,
        server_pool=server_pool
    )

    return {
        "message": "Scorul a fost înregistrat și bracket-ul a fost avansat automat!",
        "result": result
    }

@router.post("/webhook/match-result")
async def webhook_match_result(body: WebhookScoreReport, request: Request):
    """
    Automated webhook endpoint called by game server AMX Mod X / SourceMod plugins.
    """
    if body.token != WEBHOOK_SECRET_TOKEN:
        raise HTTPException(status_code=403, detail="Invalid webhook token")

    db = request.app.state.db
    server_pool = TournamentServerPool(db)

    match = await db.idg_matches.find_one({"id": body.match_id})
    if not match:
        raise HTTPException(status_code=404, detail="Match ID not found")

    score1 = body.ct_score if body.ct_team == "team1" else body.t_score
    score2 = body.t_score if body.ct_team == "team1" else body.ct_score
    win_slot = "team1" if score1 > score2 else "team2"

    result = await advance_bracket_winner(
        db=db,
        match_id=body.match_id,
        winning_team_slot=win_slot,
        score1=score1,
        score2=score2,
        server_pool=server_pool
    )

    return {"ok": True, "message": "Scorul a fost procesat automat prin webhook.", "result": result}

@router.post("/matches/{id}/checkin")
async def match_checkin(id: str, request: Request, user = Depends(require_auth)):
    """Allows players/captains to check-in for their upcoming match."""
    db = request.app.state.db
    match = await db.idg_matches.find_one({"id": id})
    if not match:
        raise HTTPException(status_code=404, detail="Meciul nu a fost găsit.")
        
    checkins = match.get("checkins", [])
    if user["username"] not in checkins:
        checkins.append(user["username"])
        await db.idg_matches.update_one({"id": id}, {"$set": {"checkins": checkins}})
        
    return {"message": f"Check-in confirmat pentru {user['username']}!", "total_checkins": len(checkins)}

@router.post("")
async def create_tournament(body: TournamentCreate, request: Request, user = Depends(require_admin)):
    db = request.app.state.db
    t_id = f"tourn-{uuid.uuid4().hex[:8]}"
    doc = {
        "id": t_id,
        "title": body.title,
        "game": body.game,
        "prize_pool": body.prize_pool,
        "max_teams": body.max_teams,
        "enrolled_teams": [],
        "format": body.format,
        "status": "registration",
        "start_date": body.start_date,
        "banner_url": body.banner_url or "https://images.unsplash.com/photo-1542751371-adc38448a05e?auto=format&fit=crop&w=1200&q=80",
        "description": body.description,
        "rules": body.rules or "Regulament standard INDUNGI.",
        "stream_url": body.stream_url or "",
        "created_at": now_iso()
    }
    await db.idg_tournaments.insert_one(doc)
    doc.pop("_id", None)
    return doc

@router.post("/{id}/register")
async def register_team_for_tournament(id: str, request: Request, user = Depends(require_auth)):
    db = request.app.state.db
    t = await db.idg_tournaments.find_one({"id": id})
    if not t:
        raise HTTPException(status_code=404, detail="Turneul nu a fost găsit.")
        
    clan = await db.idg_clans.find_one({"leader_id": user["id"]})
    if not clan:
        clan = await db.idg_clans.find_one({"members.user_id": user["id"]})
        
    team_name = clan.get("name") if clan else f"Echipa {user['username']}"
    team_tag = clan.get("tag") if clan else "IDG"
    team_logo = clan.get("avatar_url") if clan else f"https://api.dicebear.com/7.x/identicon/svg?seed={user['username']}"
    team_id = clan.get("id") if clan else f"team-{user['id']}"
    
    enrolled = t.get("enrolled_teams", [])
    if any(e.get("id") == team_id or e.get("name") == team_name for e in enrolled):
        raise HTTPException(status_code=400, detail="Echipa ta este deja înscrisă la acest turneu!")
        
    if len(enrolled) >= t.get("max_teams", 16):
        raise HTTPException(status_code=400, detail="Turneul a atins numărul maxim de echipe înscrise.")
        
    enrolled.append({
        "id": team_id,
        "name": team_name,
        "tag": team_tag,
        "logo": team_logo,
        "leader_id": user["id"],
        "registered_by": user["username"],
        "registered_at": now_iso()
    })
    
    await db.idg_tournaments.update_one({"id": id}, {"$set": {"enrolled_teams": enrolled}})
    return {"message": f"Echipa [{team_tag}] {team_name} a fost înscrisă cu succes la turneu!", "enrolled_count": len(enrolled)}

@router.delete("/matches/{id}")
async def delete_match(id: str, request: Request, user = Depends(require_admin)):
    db = request.app.state.db
    await db.idg_matches.delete_one({"id": id})
    return {"message": "Meciul a fost șters."}

@router.delete("/{id}")
async def delete_tournament(id: str, request: Request, user = Depends(require_admin)):
    db = request.app.state.db
    await db.idg_tournaments.delete_one({"id": id})
    await db.idg_matches.delete_many({"tournament_id": id})
    return {"message": "Turneul și meciurile asociate au fost șterse."}
