"""Pattern extraction utilities.

This module provides centralized regex pattern matching and extraction logic
for common patterns found in media filenames.
"""

import logging
import re
from typing import Optional, Dict
from datetime import datetime

from renamer.constants import MOVIE_DB_DICT


logger = logging.getLogger(__name__)


class PatternExtractor:
    """Shared regex pattern extraction logic.

    This class centralizes pattern matching for:
    - Movie database IDs (TMDB, IMDB, etc.)
    - Year detection and validation
    - Quality indicators
    - Source indicators

    Example:
        >>> extractor = PatternExtractor()
        >>> db_info = extractor.extract_movie_db_ids("[tmdbid-12345]")
        >>> print(db_info)  # {'type': 'tmdb', 'id': '12345'}
    """

    # Year validation constants
    CURRENT_YEAR = datetime.now().year
    YEAR_FUTURE_BUFFER = 10  # Allow up to 10 years in the future
    MIN_VALID_YEAR = 1900

    # Common quality indicators
    QUALITY_PATTERNS = {
        '2160p', '1080p', '720p', '480p', '360p', '240p', '144p',
        '4K', '8K', 'SD', 'HD', 'UHD', 'QHD', 'LQ'
    }

    # Source indicators
    SOURCE_PATTERNS = {
        'BluRay', 'BDRip', 'BRRip', 'DVDRip', 'WEB-DL', 'WEBRip',
        'HDTV', 'PDTV', 'HDRip', 'CAM', 'TS', 'TC', 'R5', 'DVD'
    }

    def __init__(self):
        """Initialize the pattern extractor."""
        self.max_valid_year = self.CURRENT_YEAR + self.YEAR_FUTURE_BUFFER

    def extract_movie_db_ids(self, text: str) -> Optional[dict[str, str]]:
        """Extract movie database IDs from text.

        Supports patterns like:
        - [tmdbid-123456]
        - {imdb-tt1234567}
        - [imdbid-tt123]

        Args:
            text: Text to search for database IDs

        Returns:
            Dictionary with 'type' and 'id' keys, or None if not found

        Example:
            >>> extractor = PatternExtractor()
            >>> extractor.extract_movie_db_ids("[tmdbid-12345]")
            {'type': 'tmdb', 'id': '12345'}
        """
        # Match patterns like [tmdbid-123456] or {imdb-tt1234567}
        pattern = r'[\[\{]([a-zA-Z]+(?:id)?)[-\s]*([a-zA-Z0-9]+)[\]\}]'
        matches = re.findall(pattern, text)

        if matches:
            # Take the last match (closest to end of filename)
            db_type, db_id = matches[-1]

            # Normalize database type
            db_type_lower = db_type.lower()

            for db_key, db_info in MOVIE_DB_DICT.items():
                if any(db_type_lower.startswith(pattern.rstrip('-'))
                       for pattern in db_info['patterns']):
                    return {'type': db_key, 'id': db_id}

        return None

    def extract_year(self, text: str, validate: bool = True) -> Optional[str]:
        """Extract year from text with optional validation.

        Looks for 4-digit years in parentheses or standalone.
        Validates that the year is within a reasonable range.

        Args:
            text: Text to extract year from
            validate: If True, validate year is within MIN_VALID_YEAR and max_valid_year

        Returns:
            Year as string (e.g., "2024") or None if not found/invalid

        Example:
            >>> extractor = PatternExtractor()
            >>> extractor.extract_year("Movie Title (2024)")
            '2024'
            >>> extractor.extract_year("Movie (1899)")  # Too old
            None
        """
        # Look for year in parentheses first (most common)
        year_pattern = r'\((\d{4})\)'
        match = re.search(year_pattern, text)

        if match:
            year = match.group(1)
            if validate:
                year_int = int(year)
                if self.MIN_VALID_YEAR <= year_int <= self.max_valid_year:
                    return year
                else:
                    logger.debug(f"Year {year} outside valid range "
                               f"{self.MIN_VALID_YEAR}-{self.max_valid_year}")
                    return None
            return year

        # Fall back to standalone 4-digit number
        standalone_pattern = r'\b(\d{4})\b'
        matches = re.findall(standalone_pattern, text)

        for potential_year in matches:
            if validate:
                year_int = int(potential_year)
                if self.MIN_VALID_YEAR <= year_int <= self.max_valid_year:
                    return potential_year
            else:
                return potential_year

        return None

    def find_year_position(self, text: str) -> Optional[int]:
        """Find the position of the year in text.

        Args:
            text: Text to search

        Returns:
            Character index of the year, or None if not found

        Example:
            >>> extractor = PatternExtractor()
            >>> extractor.find_year_position("Movie (2024) 1080p")
            6  # Position of '(' before year
        """
        year_pattern = r'\((\d{4})\)'
        match = re.search(year_pattern, text)

        if match:
            year = match.group(1)
            year_int = int(year)
            if self.MIN_VALID_YEAR <= year_int <= self.max_valid_year:
                return match.start()

        return None

    def extract_quality(self, text: str) -> Optional[str]:
        """Extract quality indicator from text.

        Args:
            text: Text to search

        Returns:
            Quality string (e.g., "1080p") or None

        Example:
            >>> extractor = PatternExtractor()
            >>> extractor.extract_quality("Movie.1080p.BluRay")
            '1080p'
        """
        text_upper = text.upper()

        for quality in self.QUALITY_PATTERNS:
            # Case-insensitive search
            pattern = r'\b' + re.escape(quality) + r'\b'
            if re.search(pattern, text_upper, re.IGNORECASE):
                return quality

        return None

    def find_quality_position(self, text: str) -> Optional[int]:
        """Find the position of quality indicator in text.

        Args:
            text: Text to search

        Returns:
            Character index of quality indicator, or None if not found

        Example:
            >>> extractor = PatternExtractor()
            >>> extractor.find_quality_position("Movie 1080p BluRay")
            6
        """
        for quality in self.QUALITY_PATTERNS:
            pattern = r'\b' + re.escape(quality) + r'\b'
            match = re.search(pattern, text, re.IGNORECASE)
            if match:
                return match.start()

        return None

    def extract_source(self, text: str) -> Optional[str]:
        """Extract source indicator from text.

        Args:
            text: Text to search

        Returns:
            Source string (e.g., "BluRay") or None

        Example:
            >>> extractor = PatternExtractor()
            >>> extractor.extract_source("Movie.BluRay.1080p")
            'BluRay'
        """
        for source in self.SOURCE_PATTERNS:
            pattern = r'\b' + re.escape(source) + r'\b'
            if re.search(pattern, text, re.IGNORECASE):
                return source

        return None

    def find_source_position(self, text: str) -> Optional[int]:
        """Find the position of source indicator in text.

        Args:
            text: Text to search

        Returns:
            Character index of source indicator, or None if not found

        Example:
            >>> extractor = PatternExtractor()
            >>> extractor.find_source_position("Movie BluRay 1080p")
            6
        """
        for source in self.SOURCE_PATTERNS:
            pattern = r'\b' + re.escape(source) + r'\b'
            match = re.search(pattern, text, re.IGNORECASE)
            if match:
                return match.start()

        return None

    def extract_bracketed_content(self, text: str) -> list[str]:
        """Extract all content from square brackets.

        Args:
            text: Text to search

        Returns:
            List of strings found in brackets

        Example:
            >>> extractor = PatternExtractor()
            >>> extractor.extract_bracketed_content("[UKR] Movie [ENG]")
            ['UKR', 'ENG']
        """
        bracket_pattern = r'\[([^\]]+)\]'
        return re.findall(bracket_pattern, text)

    def remove_bracketed_content(self, text: str) -> str:
        """Remove all bracketed content from text.

        Args:
            text: Text to clean

        Returns:
            Text with brackets and their content removed

        Example:
            >>> extractor = PatternExtractor()
            >>> extractor.remove_bracketed_content("[UKR] Movie [ENG]")
            ' Movie '
        """
        return re.sub(r'\[([^\]]+)\]', '', text)

    def split_on_delimiters(self, text: str) -> list[str]:
        """Split text on common delimiters (dots, spaces, underscores).

        Args:
            text: Text to split

        Returns:
            List of parts

        Example:
            >>> extractor = PatternExtractor()
            >>> extractor.split_on_delimiters("Movie.Title.2024")
            ['Movie', 'Title', '2024']
        """
        return re.split(r'[.\s_]+', text)

    def sanitize_for_regex(self, text: str) -> str:
        """Escape special regex characters in text.

        Args:
            text: Text to sanitize

        Returns:
            Escaped text safe for use in regex patterns

        Example:
            >>> extractor = PatternExtractor()
            >>> extractor.sanitize_for_regex("Movie (2024)")
            'Movie \\(2024\\)'
        """
        return re.escape(text)

    def is_quality_indicator(self, text: str) -> bool:
        """Check if text is a quality indicator.

        Args:
            text: Text to check

        Returns:
            True if text is a known quality indicator

        Example:
            >>> extractor = PatternExtractor()
            >>> extractor.is_quality_indicator("1080p")
            True
        """
        return text.upper() in self.QUALITY_PATTERNS

    def is_source_indicator(self, text: str) -> bool:
        """Check if text is a source indicator.

        Args:
            text: Text to check

        Returns:
            True if text is a known source indicator

        Example:
            >>> extractor = PatternExtractor()
            >>> extractor.is_source_indicator("BluRay")
            True
        """
        return any(source.lower() == text.lower() for source in self.SOURCE_PATTERNS)
