#!/usr/bin/env python3
"""trakt-cli — Trakt.tv media discovery from the terminal.

Discover trending, anticipated, and popular movies and TV shows.
Calendar of upcoming releases. Uses Trakt.tv API with Client ID.
"""

import argparse, json, os, sys, warnings
from typing import Any, Dict, List, Optional, Tuple
warnings.simplefilter("ignore")
import requests

ENV_CLIENT_ID = os.getenv("TRAKT_CLIENT_ID", "")
API_BASE = "https://api.trakt.tv"

QUIET = False
GLOBAL_FLAGS: Dict[str, Any] = {"json": False, "dry_run": False, "quiet": False, "verbose": False}
def log(m):    global QUIET; (not QUIET and not GLOBAL_FLAGS.get("json")) and print(m)
def warn(m):   print(f"Warning: {m}", file=sys.stderr)
def die(m, c=1): print(f"Error: {m}", file=sys.stderr); sys.exit(c)
def emit(h, d):
    if GLOBAL_FLAGS.get("json"): print(json.dumps(d, default=str))
    else: print(h)

def _preparse(argv):
    BOOLS = {"--json","--dry-run","--quiet","--verbose"}
    f, fl = {}, [argv[0]]
    i = 1
    while i < len(argv):
        a = argv[i]
        if a in BOOLS: f[a.lstrip("-").replace("-","_")] = True; i += 1
        elif a in ("--help","-h"): return f, argv
        elif a == "--": fl.extend(argv[i:]); break
        else: fl.append(a); i += 1
    return f, fl

class TraktClient:
    def __init__(self, client_id="", dry_run=False):
        self.client_id = client_id or ENV_CLIENT_ID
        self.dry_run = dry_run
    def _get(self, path, params=None):
        url = f"{API_BASE}{path}"
        if self.dry_run: return [{"dry_run":True, "url":url, "params":params}]
        if not self.client_id: die("TRAKT_CLIENT_ID not set. Get one from trakt.tv/oauth/applications.")
        try:
            r = requests.get(url, params=params, headers={
                "Content-Type": "application/json", "trakt-api-version": "2",
                "trakt-api-key": self.client_id}, timeout=30)
        except ConnectionError as e: die(f"Cannot connect: {e}")
        if r.status_code == 401: die("Auth failed (401). Check TRAKT_CLIENT_ID.")
        if r.status_code >= 400:
            try: d = r.json()
            except: d = r.text[:200]
            die(f"API error ({r.status_code}): {d}")
        return r.json()
    def movie_trending(self, page=1, limit=10):
        return self._get("/movies/trending", {"page":page, "limit":limit})
    def movie_anticipated(self, page=1, limit=10):
        return self._get("/movies/anticipated", {"page":page, "limit":limit})
    def movie_popular(self, page=1, limit=10):
        return self._get("/movies/popular", {"page":page, "limit":limit})
    def movie_calendar(self, start_date="", days=7):
        params = {"days":days}
        if start_date: params["start_date"] = start_date
        return self._get("/calendars/my/movies", params)
    def tv_trending(self, page=1, limit=10):
        return self._get("/shows/trending", {"page":page, "limit":limit})
    def tv_anticipated(self, page=1, limit=10):
        return self._get("/shows/anticipated", {"page":page, "limit":limit})
    def tv_popular(self, page=1, limit=10):
        return self._get("/shows/popular", {"page":page, "limit":limit})
    def tv_calendar(self, start_date="", days=7):
        params = {"days":days}
        if start_date: params["start_date"] = start_date
        return self._get("/calendars/my/shows", params)

def fmt_movie(d, idx=None):
    m = d.get("movie", d)
    t = m.get("title","?"); y = m.get("year","")
    ids = m.get("ids",{}); tmdb = ids.get("tmdb","?")
    tagline = m.get("tagline","")
    s = f"  {'%d. '%idx if idx else ''}{t:40} ({y})  tmdb={tmdb}"
    if tagline: s += f"\n  {'':4}{tagline[:80]}"
    return s

def fmt_show(d, idx=None):
    s = d.get("show", d)
    t = s.get("title","?"); y = s.get("year","")
    ids = s.get("ids",{}); tvdb = ids.get("tvdb","?")
    net = s.get("network",""); net_str = f" [{net}]" if net else ""
    s_out = f"  {'%d. '%idx if idx else ''}{t:35}{net_str} ({y})  tvdb={tvdb}"
    return s_out

def cmd_movie(client, args, endpoint):
    p = argparse.ArgumentParser(prog=f"trakt movie {endpoint}")
    p.add_argument("--limit", type=int, default=10)
    parsed, _ = p.parse_known_args(args)
    if client.dry_run: return emit(f"[dry-run] Movie {endpoint}", {"dry_run":True})
    fn = getattr(client, f"movie_{endpoint}")
    data = fn(limit=parsed.limit) or []
    if not data: return emit(f"No {endpoint} movies.", {"movies":[]})
    lines = [fmt_movie(d, i+1) for i, d in enumerate(data[:parsed.limit])]
    emit(f"{endpoint.title()} movies:\n"+"\n".join(lines), {"movies":data[:parsed.limit]})

def cmd_tv(client, args, endpoint):
    p = argparse.ArgumentParser(prog=f"trakt tv {endpoint}")
    p.add_argument("--limit", type=int, default=10)
    parsed, _ = p.parse_known_args(args)
    if client.dry_run: return emit(f"[dry-run] TV {endpoint}", {"dry_run":True})
    fn = getattr(client, f"tv_{endpoint}")
    data = fn(limit=parsed.limit) or []
    if not data: return emit(f"No {endpoint} TV shows.", {"shows":[]})
    lines = [fmt_show(d, i+1) for i, d in enumerate(data[:parsed.limit])]
    emit(f"{endpoint.title()} TV:\n"+"\n".join(lines), {"shows":data[:parsed.limit]})

def main():
    global GLOBAL_FLAGS, QUIET
    GLOBAL_FLAGS, filtered_argv = _preparse(sys.argv)
    if GLOBAL_FLAGS.get("quiet"): QUIET = True
    if GLOBAL_FLAGS.get("json"): warnings.simplefilter("ignore")
    parser = argparse.ArgumentParser(prog="trakt", description="Trakt.tv media discovery.",
        epilog="Set TRAKT_CLIENT_ID. Get one at trakt.tv/oauth/applications.")
    sub = parser.add_subparsers(dest="resource")
    mp = sub.add_parser("movie", help="Movie discovery")
    ms = mp.add_subparsers(dest="action")
    for a in ["trending","anticipated","popular"]:
        ms.add_parser(a, help=f"{a.title()} movies").add_argument("--limit", type=int, default=10)
    tp = sub.add_parser("tv", help="TV discovery")
    ts = tp.add_subparsers(dest="action")
    for a in ["trending","anticipated","popular"]:
        ts.add_parser(a, help=f"{a.title()} TV").add_argument("--limit", type=int, default=10)
    args = parser.parse_args(filtered_argv[1:])
    if not args.resource: parser.print_help(); sys.exit(1)
    client = TraktClient(dry_run=GLOBAL_FLAGS.get("dry_run",False))
    remaining = filtered_argv[filtered_argv.index(args.resource)+1:]
    if args.resource == "movie":
        if args.action: cmd_movie(client, remaining, args.action)
        else: mp.print_help()
    elif args.resource == "tv":
        if args.action: cmd_tv(client, remaining, args.action)
        else: tp.print_help()
    else: parser.print_help()

if __name__ == "__main__": main()
