#!/usr/bin/env python3
"""
Enhanced Sonarr API client for TV show metadata and episode management
"""
import json
import time
from datetime import datetime, timezone
from typing import Dict, Any, List, Optional
from urllib.parse import urlencode
from urllib.request import Request as UrlRequest, urlopen
from urllib.error import URLError, HTTPError

from core.logging import _log


class SonarrClient:
    """Enhanced Sonarr API client for TV series and episode management"""
    
    def __init__(self, base_url: str, api_key: str, timeout: int = 45, retries: int = 3):
        self.base_url = base_url.rstrip("/")
        self.api_key = api_key
        self.timeout = timeout
        self.retries = max(0, retries)
        self.enabled = bool(self.base_url and self.api_key)

    def _get(self, path: str, params: Dict[str, Any] = None) -> Optional[Any]:
        """Make GET request to Sonarr API with retries"""
        if not self.enabled:
            return None
            
        url = f"{self.base_url}/api/v3{path}"
        if params:
            url += "?" + urlencode(params)
            
        headers = {"X-Api-Key": self.api_key}
        
        for attempt in range(self.retries):
            try:
                _log("DEBUG", f"Sonarr API Request: {url}")
                req = UrlRequest(url, headers=headers)
                
                with urlopen(req, timeout=self.timeout) as resp:
                    data = resp.read().decode("utf-8")
                    result = json.loads(data) if data else None
                    return result
                    
            except HTTPError as e:
                if e.code == 401:
                    _log("ERROR", "Sonarr authentication failed - check API key")
                    return None
                elif e.code == 429:
                    wait_time = (attempt + 1) * 2
                    _log("WARNING", f"Sonarr rate limited, waiting {wait_time}s (attempt {attempt+1}/{self.retries})")
                    time.sleep(wait_time)
                else:
                    _log("WARNING", f"Sonarr HTTP {e.code} error on attempt {attempt+1}/{self.retries}: {e.reason}")
                    
            except Exception as e:
                _log("WARNING", f"Sonarr API attempt {attempt+1}/{self.retries} failed: {e}")
            
            if attempt < self.retries - 1:
                time.sleep(0.5 * (attempt + 1))
        
        _log("ERROR", f"Sonarr API failed after {self.retries} attempts: {url}")
        return None

    def series_by_imdb(self, imdb_id: str) -> Optional[Dict[str, Any]]:
        """Find series by IMDb ID using lookup endpoint"""
        search_term = f"imdbid:{imdb_id}"
        _log("DEBUG", f"Searching Sonarr with term: {search_term}")
        
        result = self._get("/series/lookup", {"term": search_term})
        if not result:
            _log("WARNING", f"No results from Sonarr lookup for: {search_term}")
            return None
            
        _log("DEBUG", f"Sonarr lookup returned {len(result)} results")
        
        # Log all results for debugging
        for i, series in enumerate(result):
            series_imdb = series.get("imdbId", "")
            series_title = series.get("title", "")
            series_id = series.get("id", "")
            _log("DEBUG", f"Result {i+1}: Title='{series_title}', IMDb='{series_imdb}', ID={series_id}")
        
        # Find exact IMDb match (case insensitive)
        target_imdb = imdb_id.lower()
        for series in result:
            series_imdb = (series.get("imdbId") or "").lower()
            if series_imdb == target_imdb:
                _log("INFO", f"Found exact IMDb match: {series.get('title')} (ID: {series.get('id')})")
                return series
                
        # Try partial match as fallback
        for series in result:
            series_imdb = (series.get("imdbId") or "").lower()
            if target_imdb in series_imdb or series_imdb in target_imdb:
                _log("WARNING", f"Found partial IMDb match: {series.get('title')} (Expected: {imdb_id}, Found: {series.get('imdbId')})")
                return series
        
        _log("WARNING", f"No IMDb match found in {len(result)} results for {imdb_id}")
        return None

    def series_by_title(self, title: str) -> Optional[Dict[str, Any]]:
        """Search for series by title as fallback when IMDb lookup fails"""
        _log("DEBUG", f"Searching Sonarr by title: {title}")
        
        result = self._get("/series/lookup", {"term": title})
        if not result:
            _log("WARNING", f"No results from Sonarr title search for: {title}")
            return None
            
        _log("DEBUG", f"Sonarr title search returned {len(result)} results")
        
        title_lower = title.lower()
        
        # Look for exact title match
        for series in result:
            series_title = (series.get("title") or "").lower()
            if series_title == title_lower:
                _log("INFO", f"Found exact title match: {series.get('title')} (ID: {series.get('id')})")
                return series
        
        # Look for partial title match
        for series in result:
            series_title = (series.get("title") or "").lower()
            if title_lower in series_title or series_title in title_lower:
                _log("INFO", f"Found partial title match: '{series.get('title')}' for search '{title}' (ID: {series.get('id')})")
                return series
        
        _log("WARNING", f"No title match found for: {title}")
        return None

    def get_all_series(self) -> List[Dict[str, Any]]:
        """Get all series from Sonarr"""
        return self._get("/series") or []

    def series_by_imdb_direct(self, imdb_id: str) -> Optional[Dict[str, Any]]:
        """Find series by scanning all series for IMDb match (slower but more reliable)"""
        _log("DEBUG", f"Direct series lookup for IMDb: {imdb_id}")
        all_series = self.get_all_series()
        
        target_imdb = imdb_id.lower()
        for series in all_series:
            series_imdb = (series.get("imdbId") or "").lower()
            if series_imdb == target_imdb:
                _log("INFO", f"Found series via direct lookup: {series.get('title')} (ID: {series.get('id')})")
                return series
        
        _log("WARNING", f"No series found with IMDb ID via direct lookup: {imdb_id}")
        return None

    def episodes_for_series(self, series_id: int) -> List[Dict[str, Any]]:
        """Get all episodes for a series"""
        return self._get("/episode", {"seriesId": series_id}) or []

    def episode_file(self, episode_file_id: int) -> Optional[Dict[str, Any]]:
        """Get episode file details"""
        return self._get(f"/episodefile/{episode_file_id}")
    
    def get_episode_import_history(self, episode_id: int) -> Optional[str]:
        """
        Get the original import date from history with enhanced detection.
        Focuses on finding the earliest REAL import, not upgrades.
        """
        all_records = []
        page = 1
        page_size = 100
        
        # Collect all history records for this episode
        while True:
            history = self._get("/history", {
                "episodeId": episode_id, 
                "sortKey": "date", 
                "sortDir": "asc",
                "page": page,
                "pageSize": page_size
            })
            
            if not history:
                break
                
            records = history.get("records", []) if isinstance(history, dict) else []
            if not records:
                break
                
            all_records.extend(records)
            
            if len(records) < page_size:
                break
                
            page += 1
            if page > 10:  # Safety valve
                break
        
        _log("DEBUG", f"Got {len(all_records)} history records for episode {episode_id}")
        
        # Categorize events
        import_events = []
        grabbed_events = []
        rename_events = []
        
        for event in all_records:
            event_type = event.get("eventType", "").lower()
            date = event.get("date")
            
            if not date:
                continue
            
            _log("DEBUG", f"History event: {event_type} at {date}")
            
            if event_type == "downloadfolderimported":
                import_events.append({"date": date, "event": event})
            elif event_type == "grabbed":
                grabbed_events.append({"date": date, "event": event})
            elif event_type == "episodefilerenamed":
                rename_events.append({"date": date, "event": event})
        
        # Use the earliest real import event
        if import_events:
            earliest_import = min(import_events, key=lambda x: x["date"])
            import_date = earliest_import["date"]
            _log("INFO", f"Found import date: {import_date} for episode {episode_id}")
            
            # Check if this looks like an upgrade by comparing to renames
            if rename_events:
                earliest_rename = min(rename_events, key=lambda x: x["date"])
                rename_date = earliest_rename["date"]
                
                try:
                    import_dt = datetime.fromisoformat(import_date.replace("Z", "+00:00"))
                    rename_dt = datetime.fromisoformat(rename_date.replace("Z", "+00:00"))
                    days_diff = (import_dt - rename_dt).days
                    
                    # If import is significantly after rename, prefer rename date
                    if days_diff > 30:
                        _log("WARNING", f"Import {import_date} is {days_diff} days after rename {rename_date} - using rename date")
                        return rename_date
                        
                except Exception as e:
                    _log("DEBUG", f"Error comparing dates: {e}")
            
            return import_date
        
        # Fallback to grab event
        if grabbed_events:
            earliest_grab = min(grabbed_events, key=lambda x: x["date"])
            _log("WARNING", f"No import events, using grab date: {earliest_grab['date']} for episode {episode_id}")
            return earliest_grab["date"]
        
        _log("WARNING", f"No reliable import events found for episode {episode_id}")
        return None


if __name__ == "__main__":
    # Test the client
    import os
    
    base_url = os.environ.get("SONARR_URL", "")
    api_key = os.environ.get("SONARR_API_KEY", "")
    
    if base_url and api_key:
        client = SonarrClient(base_url, api_key)
        
        # Test with a known series
        test_imdb = "tt2085059"  # Example
        series = client.series_by_imdb(test_imdb)
        
        if series:
            series_id = series.get("id")
            print(f"Found series: {series.get('title')} (ID: {series_id})")
            
            # Get episodes
            episodes = client.episodes_for_series(series_id)
            print(f"Found {len(episodes)} episodes")
            
            # Test import date for first episode
            if episodes:
                first_episode = episodes[0]
                episode_id = first_episode.get("id")
                import_date = client.get_episode_import_history(episode_id)
                print(f"First episode import date: {import_date}")
        else:
            print(f"Series not found: {test_imdb}")
    else:
        print("Please set SONARR_URL and SONARR_API_KEY environment variables for testing")