#!/usr/bin/env python3
"""
Generate & auto-refresh M3U playlist from cdnlivetv.tv sports events API.

- Groups events by tournament (Soccer, etc.) and country.
- Uses team images when available, channel image otherwise.
- Wraps player URLs in the proxy.
- Rebuilds the playlist every REFRESH_SECONDS (default 240 = 4 min).
- Labels events as [Live] or [Upcoming] based on start/end times.
"""

import json
import os
import sys
import time
import urllib.parse
from datetime import datetime, timezone
from urllib.request import urlopen, Request

EVENTS_URL = "https://api.cdnlivetv.tv/api/v1/events/sports/?user=cdnlivetv&plan=free"
PROXY_BASE = "https://easyproxy.surpriseplay.online/proxy/hls/manifest.m3u8?d="

REFRESH_SECONDS = 240  # 4 minutes
OUT_FILE = sys.argv[1] if len(sys.argv) > 1 else "events.m3u"


# ---------- helpers ----------

def fetch_json(url: str) -> dict:
    req = Request(url, headers={
        "User-Agent": "Mozilla/5.0",
        "Accept": "application/json",
    })
    with urlopen(req, timeout=30) as resp:
        return json.loads(resp.read().decode("utf-8"))


def make_proxy_url(player_url: str) -> str:
    return PROXY_BASE + urllib.parse.quote(player_url, safe="")


def sanitize(text: str) -> str:
    return (text or "").replace("\n", " ").replace("\r", " ").strip()


def iso_now() -> str:
    return datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%SZ")


def pick_logo(*candidates) -> str:
    for c in candidates:
        if c:
            c = c.strip()
            if c:
                return c
    return ""


def parse_utc(ts: str):
    """Parse 'YYYY-MM-DD HH:MM' as UTC. Returns None on failure."""
    if not ts:
        return None
    try:
        return datetime.strptime(ts, "%Y-%m-%d %H:%M").replace(tzinfo=timezone.utc)
    except Exception:
        return None


def event_phase(event: dict) -> str:
    """
    Return 'Upcoming', 'Live', or '' (ended/unknown).
    Uses start/end timestamps when possible; falls back to status field.
      - start > now            -> Upcoming
      - start <= now < end     -> Live
      - end <= now             -> '' (ended, will be filtered by is_live_or_upcoming)
    Falls back to status: 'NS' -> Upcoming, otherwise Live.
    """
    now = datetime.now(timezone.utc)
    start = parse_utc(event.get("start", ""))
    end = parse_utc(event.get("end", ""))

    if start and end:
        if now < start:
            return "Upcoming"
        if start <= now < end:
            return "Live"
        return ""
    if start and not end:
        return "Upcoming" if now < start else "Live"
    if not start and end:
        return "Live" if now < end else ""

    # No timestamps: fall back to status field
    status = (event.get("status") or "").upper()
    if status == "NS":
        return "Upcoming"
    return "Live"


def is_live_or_upcoming(event: dict) -> bool:
    """
    Keep events whose end time is in the future.
    Falls back to keeping the event if the timestamp is unparseable.
    """
    end = parse_utc(event.get("end", "")) or parse_utc(event.get("start", ""))
    if end is None:
        return True
    return end >= datetime.now(timezone.utc)


# ---------- M3U builder ----------

def build_m3u(data: dict) -> tuple[str, int]:
    """
    Returns (m3u_text, entry_count).
    Iterates over sports (Soccer, Basketball, ...) and for each event emits
    one entry per available channel.
    """
    lines = ["#EXTM3U"]
    count = 0

    sports_root = data.get("cdn-live-tv", {})
    for sport_name, events in sports_root.items():
        if not isinstance(events, list):
            continue

        for ev in events:
            if not is_live_or_upcoming(ev):
                continue

            event_title = sanitize(ev.get("event", "Unknown Match"))
            tournament   = sanitize(ev.get("tournament", sport_name))
            country      = sanitize(ev.get("country", ""))
            start        = sanitize(ev.get("start", ""))
            status       = sanitize(ev.get("status", ""))
            home_img     = pick_logo(ev.get("homeTeamIMG"))
            away_img     = pick_logo(ev.get("awayTeamIMG"))
            flag_img     = pick_logo(ev.get("countryIMG"))

            # Prefer a side/league logo; fall back to team images, then flag.
            event_logo = home_img or away_img or flag_img

            # Determine Live / Upcoming label
            phase = event_phase(ev)

            group = f"{sport_name} - {country}" if country else sport_name

            for ch in ev.get("channels", []) or []:
                ch_name = sanitize(ch.get("channel_name", "Unknown"))
                ch_url  = ch.get("url", "")
                if not ch_url:
                    continue

                logo = pick_logo(ch.get("image"), event_logo)

                base_name = f"[{start}] {event_title} ({ch_name})"
                if phase:
                    display_name = f"[{phase}] {base_name}"
                else:
                    display_name = base_name

                tvg_id = f"{ev.get('gameID', '')}-{ch.get('id', '')}".strip("-")

                lines.append(
                    f'#EXTINF:-1 tvg-id="{tvg_id}" '
                    f'tvg-name="{sanitize(event_title)}" '
                    f'tvg-logo="{logo}" '
                    f'group-title="{group}",{display_name}'
                )
                lines.append(make_proxy_url(ch_url))
                count += 1

    return "\n".join(lines) + "\n", count


# ---------- main loop ----------

def refresh_once() -> None:
    try:
        data = fetch_json(EVENTS_URL)
    except Exception as e:
        print(f"[{iso_now()}] Fetch error: {e}", file=sys.stderr)
        return

    m3u, count = build_m3u(data)
    tmp = OUT_FILE + ".tmp"
    with open(tmp, "w", encoding="utf-8") as f:
        f.write(m3u)
    # atomic-ish replace so players never read a half-written file
    os.replace(tmp, OUT_FILE)

    print(f"[{iso_now()}] Wrote {OUT_FILE} — {count} entries", file=sys.stderr)


def main() -> None:
    print(f"Starting refresh loop every {REFRESH_SECONDS}s → {OUT_FILE}", file=sys.stderr)
    while True:
        refresh_once()
        try:
            time.sleep(REFRESH_SECONDS)
        except KeyboardInterrupt:
            print("\nStopped.", file=sys.stderr)
            break


if __name__ == "__main__":
    main()