"""Service for managing series with NFO metadata support.

This service layer component orchestrates SerieList (core entity) with
NFOService to provide automatic NFO creation and updates during series scans.

This follows clean architecture principles by keeping the core entities
independent of external services like TMDB API.
"""

import asyncio
import logging
from pathlib import Path
from typing import Optional

from src.config.settings import settings
from src.core.entities.SerieList import SerieList
from src.core.services.nfo_service import NFOService
from src.core.services.tmdb_client import TMDBAPIError

logger = logging.getLogger(__name__)


class SeriesManagerService:
    """Service for managing series with optional NFO metadata support.
    
    This service wraps SerieList and adds NFO creation/update capabilities
    based on configuration settings. It maintains clean separation between
    core entities and external services.
    
    Attributes:
        serie_list: SerieList instance for series management
        nfo_service: Optional NFOService for metadata management
        auto_create_nfo: Whether to auto-create NFO files
        update_on_scan: Whether to update existing NFO files
    """
    
    def __init__(
        self,
        anime_directory: str,
        tmdb_api_key: Optional[str] = None,
        auto_create_nfo: bool = False,
        update_on_scan: bool = False,
        download_poster: bool = True,
        download_logo: bool = True,
        download_fanart: bool = True,
        image_size: str = "original"
    ):
        """Initialize series manager service.
        
        Args:
            anime_directory: Base directory for anime series
            tmdb_api_key: TMDB API key (optional, required for NFO features)
            auto_create_nfo: Automatically create NFO files when scanning
            update_on_scan: Update existing NFO files when scanning
            download_poster: Download poster.jpg
            download_logo: Download logo.png
            download_fanart: Download fanart.jpg
            image_size: Image size to download
        """
        self.anime_directory = anime_directory
        # Skip automatic folder scanning - we load from database instead
        self.serie_list = SerieList(anime_directory, skip_load=True)
        
        # NFO configuration
        self.auto_create_nfo = auto_create_nfo
        self.update_on_scan = update_on_scan
        self.download_poster = download_poster
        self.download_logo = download_logo
        self.download_fanart = download_fanart
        
        # Initialize NFO service if API key provided and NFO features enabled
        self.nfo_service: Optional[NFOService] = None
        if tmdb_api_key and (auto_create_nfo or update_on_scan):
            try:
                from src.core.services.nfo_factory import get_nfo_factory
                factory = get_nfo_factory()
                self.nfo_service = factory.create(
                    tmdb_api_key=tmdb_api_key,
                    anime_directory=anime_directory,
                    image_size=image_size,
                    auto_create=auto_create_nfo
                )
                logger.info("NFO service initialized (auto_create=%s, update=%s)",
                           auto_create_nfo, update_on_scan)
            except (ValueError, Exception) as e:  # pylint: disable=broad-except
                logger.warning(
                    "Failed to initialize NFO service: %s", str(e)
                )
                self.nfo_service = None
        elif auto_create_nfo or update_on_scan:
            logger.warning(
                "NFO features requested but TMDB_API_KEY not provided. "
                "NFO creation/updates will be skipped."
            )
    
    @classmethod
    def from_settings(cls) -> "SeriesManagerService":
        """Create SeriesManagerService from application settings.
        
        Returns:
            Configured SeriesManagerService instance
        """
        return cls(
            anime_directory=settings.anime_directory,
            tmdb_api_key=settings.tmdb_api_key,
            auto_create_nfo=settings.nfo_auto_create,
            update_on_scan=settings.nfo_update_on_scan,
            download_poster=settings.nfo_download_poster,
            download_logo=settings.nfo_download_logo,
            download_fanart=settings.nfo_download_fanart,
            image_size=settings.nfo_image_size
        )
    
    async def process_nfo_for_series(
        self,
        serie_folder: str,
        serie_name: str,
        serie_key: str,
        year: Optional[int] = None
    ):
        """Process NFO file for a series (create or update).
        
        Args:
            serie_folder: Series folder name
            serie_name: Series display name
            serie_key: Series unique identifier for database updates
            year: Release year (helps with TMDB matching)
        """
        if not self.nfo_service:
            return
        
        nfo_exists = False
        ids = {}
        
        try:
            folder_path = Path(self.anime_directory) / serie_folder
            nfo_path = folder_path / "tvshow.nfo"
            nfo_exists = await self.nfo_service.check_nfo_exists(serie_folder)
            
            # If NFO exists, parse IDs and update database
            if nfo_exists:
                logger.debug("Parsing IDs from existing NFO for '%s'", serie_name)
                ids = self.nfo_service.parse_nfo_ids(nfo_path)
                
                if ids["tmdb_id"] or ids["tvdb_id"]:
                    # Update database using service layer
                    from datetime import datetime, timezone

                    from src.server.database.connection import get_db_session
                    from src.server.database.service import AnimeSeriesService
                    
                    async with get_db_session() as db:
                        series = await AnimeSeriesService.get_by_key(db, serie_key)
                        
                        if series:
                            now = datetime.now(timezone.utc)
                            
                            # Prepare update fields
                            update_fields = {
                                "has_nfo": True,
                                "nfo_updated_at": now,
                            }
                            
                            if series.nfo_created_at is None:
                                update_fields["nfo_created_at"] = now
                            
                            if ids["tmdb_id"] is not None:
                                update_fields["tmdb_id"] = ids["tmdb_id"]
                                logger.debug(
                                    f"Updated TMDB ID for '{serie_name}': "
                                    f"{ids['tmdb_id']}"
                                )
                            
                            if ids["tvdb_id"] is not None:
                                update_fields["tvdb_id"] = ids["tvdb_id"]
                                logger.debug(
                                    f"Updated TVDB ID for '{serie_name}': "
                                    f"{ids['tvdb_id']}"
                                )
                            
                            # Use service layer for update
                            await AnimeSeriesService.update(db, series.id, **update_fields)
                            await db.commit()
                            
                            logger.info(
                                f"Updated database with IDs from NFO for "
                                f"'{serie_name}' - TMDB: {ids['tmdb_id']}, "
                                f"TVDB: {ids['tvdb_id']}"
                            )
                        else:
                            logger.warning(
                                f"Series not found in database for NFO ID "
                                f"update: {serie_key}"
                            )
            
            # Create NFO file only if it doesn't exist and auto_create enabled
            if not nfo_exists and self.auto_create_nfo:
                logger.info(
                    f"Creating NFO for '{serie_name}' ({serie_folder})"
                )
                try:
                    await self.nfo_service.create_tvshow_nfo(
                        serie_name=serie_name,
                        serie_folder=serie_folder,
                        year=year,
                        download_poster=self.download_poster,
                        download_logo=self.download_logo,
                        download_fanart=self.download_fanart
                    )
                    logger.info("Successfully created NFO for '%s'", serie_name)
                except TMDBAPIError as create_error:
                    # TMDB lookup failed, create minimal NFO to track the series
                    logger.warning(
                        "TMDB lookup failed for '%s', creating minimal NFO: %s",
                        serie_name, create_error
                    )
                    try:
                        await self.nfo_service.create_minimal_nfo(
                            serie_name=serie_name,
                            serie_folder=serie_folder,
                            year=year
                        )
                        logger.info("Created minimal NFO for '%s'", serie_name)
                    except Exception as minimal_error:
                        logger.error(
                            "Failed to create minimal NFO for '%s': %s",
                            serie_name, minimal_error
                        )
            elif nfo_exists:
                logger.debug(
                    f"NFO exists for '{serie_name}', skipping download"
                )
                    
        except TMDBAPIError as e:
            # Only log at ERROR if no NFO exists and we have no IDs
            # If NFO exists with IDs, this is just a lookup failure, log at DEBUG
            if nfo_exists and (ids.get("tmdb_id") or ids.get("tvdb_id")):
                logger.debug(
                    "TMDB API lookup failed for '%s' (has NFO with IDs): %s",
                    serie_name, e
                )
            else:
                logger.error("TMDB API error processing '%s': %s", serie_name, e)
        except Exception as e:
            logger.error(
                f"Unexpected error processing NFO for '{serie_name}': {e}",
                exc_info=True
            )
    
    async def scan_and_process_nfo(self):
        """Scan all series and process NFO files based on configuration.
        
        This method:
        1. Loads series from database (avoiding filesystem scan)
        2. For each series with existing NFO, reads TMDB/TVDB IDs
           and updates database
        3. For each series without NFO (if auto_create=True), creates one
        4. For each series with NFO (if update_on_scan=True), updates it
        5. Runs operations concurrently for better performance
        """
        if not self.nfo_service:
            logger.info("NFO service not enabled, skipping NFO processing")
            return
        
        # Import database dependencies
        from src.server.database.connection import get_db_session
        from src.server.database.service import AnimeSeriesService

        # Load series from database (not from filesystem)
        async with get_db_session() as db:
            anime_series_list = await AnimeSeriesService.get_all(
                db, with_episodes=False
            )
        
        if not anime_series_list:
            logger.info("No series found in database to process")
            return
        
        logger.info("Processing NFO for %s series...", len(anime_series_list))
        
        # Create tasks for concurrent processing
        # Each task creates its own database session
        tasks = []
        for anime_series in anime_series_list:
            # Extract year if available
            year = getattr(anime_series, 'year', None)
            
            task = self.process_nfo_for_series(
                serie_folder=anime_series.folder,
                serie_name=anime_series.name,
                serie_key=anime_series.key,
                year=year
            )
            tasks.append(task)
        
        # Process in batches to avoid overwhelming TMDB API
        batch_size = 5
        for i in range(0, len(tasks), batch_size):
            batch = tasks[i:i + batch_size]
            await asyncio.gather(*batch, return_exceptions=True)
            
            # Small delay between batches to respect rate limits
            if i + batch_size < len(tasks):
                await asyncio.sleep(2)
    
    async def close(self):
        """Clean up resources."""
        if self.nfo_service:
            await self.nfo_service.close()
