"""
NFOGuard Web Database - Lightweight Read-Only Database Access
Optimized for web interface queries with minimal dependencies
"""
import psycopg2
import psycopg2.extras
from typing import Dict, List, Optional, Any, Tuple
import logging

logger = logging.getLogger(__name__)


class WebDatabase:
    """Lightweight database access for web interface"""
    
    def __init__(self, db_type: str, host: str, port: int, database: str, user: str, password: str):
        self.db_type = db_type.lower()
        self.host = host
        self.port = port
        self.database = database
        self.user = user
        self.password = password
        self.connection = None
        
        # Connect to database
        self._connect()
    
    def _connect(self):
        """Connect to PostgreSQL database"""
        if self.db_type != "postgresql":
            raise ValueError("Web interface only supports PostgreSQL")
        
        try:
            self.connection = psycopg2.connect(
                host=self.host,
                port=self.port,
                database=self.database,
                user=self.user,
                password=self.password,
                cursor_factory=psycopg2.extras.RealDictCursor
            )
            # Set to autocommit for read operations
            self.connection.autocommit = True
            logger.info(f"Connected to PostgreSQL: {self.host}:{self.port}/{self.database}")
        except Exception as e:
            logger.error(f"Failed to connect to database: {e}")
            raise
    
    def execute_query(self, query: str, params: Optional[Tuple] = None) -> List[Dict[str, Any]]:
        """Execute a SELECT query and return results"""
        try:
            with self.connection.cursor() as cursor:
                cursor.execute(query, params)
                return [dict(row) for row in cursor.fetchall()]
        except Exception as e:
            logger.error(f"Query failed: {query[:100]}... Error: {e}")
            raise
    
    def execute_single(self, query: str, params: Optional[Tuple] = None) -> Optional[Dict[str, Any]]:
        """Execute a query and return single result"""
        results = self.execute_query(query, params)
        return results[0] if results else None
    
    def execute_scalar(self, query: str, params: Optional[Tuple] = None) -> Any:
        """Execute a query and return single value"""
        result = self.execute_single(query, params)
        return list(result.values())[0] if result else None
    
    # Dashboard Statistics
    def get_dashboard_stats(self) -> Dict[str, Any]:
        """Get dashboard statistics"""
        stats = {}
        
        # Movie statistics
        movie_query = """
            SELECT 
                COUNT(*) as total_movies,
                COUNT(CASE WHEN dateadded IS NOT NULL AND source != 'unknown' THEN 1 END) as movies_with_dates,
                COUNT(CASE WHEN dateadded IS NULL OR source = 'unknown' THEN 1 END) as movies_without_dates
            FROM movies
        """
        movie_stats = self.execute_single(movie_query)
        stats.update(movie_stats)
        
        # TV statistics
        tv_query = """
            SELECT 
                COUNT(DISTINCT imdb_id) as total_series,
                COUNT(*) as total_episodes,
                COUNT(CASE WHEN dateadded IS NOT NULL AND source != 'unknown' THEN 1 END) as episodes_with_dates,
                COUNT(CASE WHEN dateadded IS NULL OR source = 'unknown' THEN 1 END) as episodes_without_dates
            FROM episodes
        """
        tv_stats = self.execute_single(tv_query)
        stats.update(tv_stats)
        
        return stats
    
    # Movie queries
    def get_movies(self, skip: int = 0, limit: int = 50, has_date: Optional[bool] = None) -> List[Dict[str, Any]]:
        """Get movies with pagination"""
        where_clause = ""
        params = []
        
        if has_date is not None:
            if has_date:
                where_clause = "WHERE dateadded IS NOT NULL AND source != 'unknown'"
            else:
                where_clause = "WHERE dateadded IS NULL OR source = 'unknown'"
        
        query = f"""
            SELECT imdb_id, title, year, dateadded, released, source, last_updated
            FROM movies
            {where_clause}
            ORDER BY title, year
            LIMIT %s OFFSET %s
        """
        params.extend([limit, skip])
        
        return self.execute_query(query, params)
    
    def get_movie_count(self, has_date: Optional[bool] = None) -> int:
        """Get total movie count"""
        where_clause = ""
        params = []
        
        if has_date is not None:
            if has_date:
                where_clause = "WHERE dateadded IS NOT NULL AND source != 'unknown'"
            else:
                where_clause = "WHERE dateadded IS NULL OR source = 'unknown'"
        
        query = f"SELECT COUNT(*) FROM movies {where_clause}"
        return self.execute_scalar(query, params)
    
    # TV Series queries
    def get_series(self, skip: int = 0, limit: int = 50, date_filter: str = "none") -> List[Dict[str, Any]]:
        """Get TV series with episode statistics"""
        where_clause = ""
        if date_filter == "complete":
            where_clause = """
                WHERE NOT EXISTS (
                    SELECT 1 FROM episodes e2 
                    WHERE e2.imdb_id = e.imdb_id 
                    AND (e2.dateadded IS NULL OR e2.source = 'unknown')
                )
            """
        elif date_filter == "incomplete":
            where_clause = """
                WHERE EXISTS (
                    SELECT 1 FROM episodes e2 
                    WHERE e2.imdb_id = e.imdb_id 
                    AND (e2.dateadded IS NULL OR e2.source = 'unknown')
                )
            """
        
        query = f"""
            SELECT 
                e.imdb_id,
                e.series_title,
                COUNT(*) as total_episodes,
                COUNT(CASE WHEN e.dateadded IS NOT NULL AND e.source != 'unknown' THEN 1 END) as episodes_with_dates,
                COUNT(CASE WHEN e.dateadded IS NULL OR e.source = 'unknown' THEN 1 END) as episodes_without_dates,
                MAX(e.last_updated) as last_updated
            FROM episodes e
            {where_clause}
            GROUP BY e.imdb_id, e.series_title
            ORDER BY e.series_title
            LIMIT %s OFFSET %s
        """
        
        return self.execute_query(query, [limit, skip])
    
    def get_series_count(self, date_filter: str = "none") -> int:
        """Get total series count"""
        where_clause = ""
        if date_filter == "complete":
            where_clause = """
                WHERE NOT EXISTS (
                    SELECT 1 FROM episodes e2 
                    WHERE e2.imdb_id = e.imdb_id 
                    AND (e2.dateadded IS NULL OR e2.source = 'unknown')
                )
            """
        elif date_filter == "incomplete":
            where_clause = """
                WHERE EXISTS (
                    SELECT 1 FROM episodes e2 
                    WHERE e2.imdb_id = e.imdb_id 
                    AND (e2.dateadded IS NULL OR e2.source = 'unknown')
                )
            """
        
        query = f"""
            SELECT COUNT(DISTINCT imdb_id)
            FROM episodes e
            {where_clause}
        """
        
        return self.execute_scalar(query)
    
    def get_episodes_for_series(self, imdb_id: str) -> List[Dict[str, Any]]:
        """Get all episodes for a series"""
        query = """
            SELECT imdb_id, series_title, season, episode, episode_title, 
                   dateadded, source, last_updated
            FROM episodes
            WHERE imdb_id = %s
            ORDER BY season, episode
        """
        return self.execute_query(query, [imdb_id])
    
    # Source statistics
    def get_series_sources(self) -> List[Dict[str, Any]]:
        """Get source statistics for series"""
        query = """
            SELECT 
                source,
                COUNT(DISTINCT imdb_id) as series_count,
                COUNT(*) as episode_count
            FROM episodes
            WHERE source != 'unknown'
            GROUP BY source
            ORDER BY series_count DESC, episode_count DESC
        """
        return self.execute_query(query)
    
    # Episode-specific methods for web interface
    def get_episode_date(self, imdb_id: str, season: int, episode: int) -> Optional[Dict]:
        """Get episode data including dates"""
        query = """
            SELECT imdb_id, season, episode, aired, dateadded, source, has_video_file, last_updated
            FROM episodes 
            WHERE imdb_id = %s AND season = %s AND episode = %s
        """
        return self.execute_single(query, (imdb_id, season, episode))
    
    def upsert_episode_date(self, imdb_id: str, season: int, episode: int, 
                           aired: Optional[str], dateadded: Optional[str], 
                           source: str, has_video_file: bool = False) -> None:
        """Update or insert episode date information"""
        # First check if episode exists
        existing = self.get_episode_date(imdb_id, season, episode)
        
        # Temporarily disable autocommit for the transaction
        original_autocommit = self.connection.autocommit
        self.connection.autocommit = False
        
        try:
            with self.connection.cursor() as cursor:
                if existing:
                    # Update existing episode
                    query = """
                        UPDATE episodes 
                        SET aired = %s, dateadded = %s, source = %s, has_video_file = %s, last_updated = NOW()
                        WHERE imdb_id = %s AND season = %s AND episode = %s
                    """
                    cursor.execute(query, (aired, dateadded, source, has_video_file, imdb_id, season, episode))
                else:
                    # Insert new episode
                    query = """
                        INSERT INTO episodes (imdb_id, season, episode, aired, dateadded, source, has_video_file, last_updated)
                        VALUES (%s, %s, %s, %s, %s, %s, %s, NOW())
                    """
                    cursor.execute(query, (imdb_id, season, episode, aired, dateadded, source, has_video_file))
                
                self.connection.commit()
        except Exception as e:
            self.connection.rollback()
            logger.error(f"Failed to upsert episode date: {e}")
            raise
        finally:
            # Restore original autocommit setting
            self.connection.autocommit = original_autocommit
    
    def get_movie_dates(self, imdb_id: str) -> Optional[Dict]:
        """Get movie data including dates"""
        query = """
            SELECT imdb_id, path, released, dateadded, source, has_video_file, last_updated
            FROM movies 
            WHERE imdb_id = %s
        """
        return self.execute_single(query, (imdb_id,))
    
    def upsert_movie_dates(self, imdb_id: str, released: Optional[str], 
                          dateadded: Optional[str], source: str, 
                          has_video_file: bool = False, path: str = "") -> None:
        """Update or insert movie date information"""
        # First check if movie exists
        existing = self.get_movie_dates(imdb_id)
        
        # Temporarily disable autocommit for the transaction
        original_autocommit = self.connection.autocommit
        self.connection.autocommit = False
        
        try:
            with self.connection.cursor() as cursor:
                if existing:
                    # Update existing movie
                    query = """
                        UPDATE movies 
                        SET released = %s, dateadded = %s, source = %s, has_video_file = %s, last_updated = NOW()
                        WHERE imdb_id = %s
                    """
                    cursor.execute(query, (released, dateadded, source, has_video_file, imdb_id))
                else:
                    # Insert new movie
                    query = """
                        INSERT INTO movies (imdb_id, path, released, dateadded, source, has_video_file, last_updated)
                        VALUES (%s, %s, %s, %s, %s, %s, NOW())
                    """
                    cursor.execute(query, (imdb_id, path, released, dateadded, source, has_video_file))
                
                self.connection.commit()
        except Exception as e:
            self.connection.rollback()
            logger.error(f"Failed to upsert movie dates: {e}")
            raise
        finally:
            # Restore original autocommit setting
            self.connection.autocommit = original_autocommit
    
    def get_connection(self):
        """Get database connection for advanced operations"""
        return self.connection
    
    def _get_first_value(self, row):
        """Extract first value from a database row (compatibility method)"""
        if row is None:
            return None
        if isinstance(row, dict):
            return list(row.values())[0] if row else None
        return row[0] if row else None
    
    def get_stats(self) -> Dict[str, Any]:
        """Get basic database statistics (compatibility method)"""
        return self.get_dashboard_stats()
    
    def add_processing_history(self, imdb_id: str, media_type: str, event_type: str, details: Dict) -> None:
        """Add processing history entry (simplified for web interface)"""
        # Temporarily disable autocommit for the transaction
        original_autocommit = self.connection.autocommit
        self.connection.autocommit = False
        
        try:
            with self.connection.cursor() as cursor:
                # Check if processing_history table exists
                cursor.execute("""
                    SELECT EXISTS (
                        SELECT FROM information_schema.tables 
                        WHERE table_name = 'processing_history'
                    )
                """)
                table_exists = cursor.fetchone()[0]
                
                if table_exists:
                    query = """
                        INSERT INTO processing_history (imdb_id, media_type, event_type, details, processed_at)
                        VALUES (%s, %s, %s, %s, NOW())
                    """
                    import json
                    cursor.execute(query, (imdb_id, media_type, event_type, json.dumps(details)))
                    self.connection.commit()
                else:
                    # Table doesn't exist, skip logging
                    logger.debug("Processing history table not found, skipping log entry")
        except Exception as e:
            self.connection.rollback()
            logger.error(f"Failed to add processing history: {e}")
            # Don't raise, this is non-critical
        finally:
            # Restore original autocommit setting
            self.connection.autocommit = original_autocommit
    
    def close(self):
        """Close database connection"""
        if self.connection:
            self.connection.close()
            logger.info("Database connection closed")
    
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.close()