"""Filtering utilities for pushtunes sync operations."""

import re
import os
from dataclasses import dataclass
from pushtunes.models.album import Album
from pushtunes.models.track import Track


@dataclass
class FilterPattern:
    """Represents a single filter pattern."""

    field: str  # 'artist', 'album', 'year'
    pattern: str  # regex pattern
    regex: re.Pattern  # compiled regex


class AlbumFilter:
    """Album filtering system supporting regex patterns on artist/album fields."""

    def __init__(self, patterns: list[str] | None = None):
        """Initialize filter with pattern strings.

        Args:
            patterns: list of pattern strings in format "field:'regex'"
        """
        self.patterns: list[FilterPattern] = []
        if patterns:
            for pattern in patterns:
                self.add_pattern(pattern)

    def add_pattern(self, pattern_str: str) -> None:
        """Add a filter pattern.

        Args:
            pattern_str: Pattern string in format "field:'regex'"

        Raises:
            ValueError: If pattern format is invalid
        """
        pattern = self._parse_pattern(pattern_str)
        self.patterns.append(pattern)

    def _parse_pattern(self, pattern_str: str) -> FilterPattern:
        """Parse a pattern string into a FilterPattern.

        Args:
            pattern_str: Pattern string like "artist:'.*dead.*'" or "album:'.*signal.*'"

        Returns:
            FilterPattern object

        Raises:
            ValueError: If pattern format is invalid
        """
        # Pattern format: field:'regex'
        pattern_str = pattern_str.strip()

        # Match field:'regex' format
        match = re.match(r"^(artist|album|track):'(.+)'$", pattern_str, re.IGNORECASE)
        if not match:
            raise ValueError(
                f"Invalid pattern format: {pattern_str}. Expected format: field:'regex'"
            )

        field = match.group(1).lower()
        regex_str = match.group(2)

        try:
            compiled_regex = re.compile(regex_str, re.IGNORECASE)
        except re.error as e:
            raise ValueError(f"Invalid regex pattern '{regex_str}': {e}")

        return FilterPattern(field=field, pattern=regex_str, regex=compiled_regex)

    def matches(self, item) -> bool:
        """Check if an item matches any of the filter patterns.

        Args:
            item: Item to check

        Returns:
            True if item matches any pattern, False if no patterns or no matches
        """
        if not self.patterns:
            # No patterns means no filtering (include all)
            return True

        # Check each pattern - OR logic (any match includes the item)
        for pattern in self.patterns:
            if pattern.field == "artist":
                if pattern.regex.search(item.artist):
                    return True
            elif pattern.field == "album":
                # For albums, check the title
                if isinstance(item, Album):
                    if pattern.regex.search(item.title):
                        return True

        # No patterns matched
        return False

    @classmethod
    def from_string(cls, filter_string: str) -> "AlbumFilter":
        """Create filter from comma-separated pattern string.

        Args:
            filter_string: String like "artist:'.*dead.*',album:'.*signal.*'"

        Returns:
            AlbumFilter instance
        """
        if not filter_string.strip():
            return cls()

        patterns = []
        # Split on commas but respect quotes
        pattern_parts = cls._split_filter_string(filter_string)

        for part in pattern_parts:
            part = part.strip()
            if part:
                patterns.append(part)

        return cls(patterns)

    @classmethod
    def from_file(cls, file_path: str) -> "AlbumFilter":
        """Create filter from file containing patterns.

        Args:
            file_path: Path to file with one pattern per line

        Returns:
            AlbumFilter instance

        Raises:
            FileNotFoundError: If file doesn't exist
            ValueError: If file contains invalid patterns
        """
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"Filter file not found: {file_path}")

        patterns = []
        with open(file_path, "r", encoding="utf-8") as f:
            for line_num, line in enumerate(f, 1):
                line = line.strip()
                # Skip empty lines and comments
                if not line or line.startswith("#"):
                    continue

                try:
                    # Each line can contain multiple comma-separated patterns
                    line_patterns = cls._split_filter_string(line)
                    for pattern in line_patterns:
                        pattern = pattern.strip()
                        if pattern:
                            patterns.append(pattern)
                except ValueError as e:
                    raise ValueError(f"Invalid pattern on line {line_num}: {e}")

        return cls(patterns)

    @staticmethod
    def _split_filter_string(filter_string: str) -> list[str]:
        """Split filter string on commas while respecting quotes.

        Args:
            filter_string: String to split

        Returns:
            list of pattern strings
        """
        patterns = []
        current_pattern = ""
        in_quotes = False

        i = 0
        while i < len(filter_string):
            char = filter_string[i]

            if char == "'" and (i == 0 or filter_string[i - 1] != "\\"):
                in_quotes = not in_quotes
                current_pattern += char
            elif char == "," and not in_quotes:
                if current_pattern.strip():
                    patterns.append(current_pattern.strip())
                current_pattern = ""
            else:
                current_pattern += char

            i += 1

        # Add the last pattern
        if current_pattern.strip():
            patterns.append(current_pattern.strip())

        return patterns

    def get_summary(self) -> str:
        """Get a human-readable summary of the filter patterns.

        Returns:
            Summary string
        """
        if not self.patterns:
            return "No filters (all albums included)"

        summary_parts = []
        artist_patterns = []
        album_patterns = []

        for pattern in self.patterns:
            if pattern.field == "artist":
                artist_patterns.append(pattern.pattern)
            else:
                album_patterns.append(pattern.pattern)

        if artist_patterns:
            summary_parts.append(f"Artist patterns: {', '.join(artist_patterns)}")
        if album_patterns:
            summary_parts.append(f"Album patterns: {', '.join(album_patterns)}")

        return "; ".join(summary_parts)

    def __len__(self) -> int:
        """Return number of patterns."""
        return len(self.patterns)

    def __bool__(self) -> bool:
        """Return True if filter has patterns."""
        return bool(self.patterns)


class TrackFilter:
    """Track filtering system supporting regex patterns on artist/track/album fields."""

    def __init__(self, patterns: list[str] | None = None):
        """Initialize filter with pattern strings.

        Args:
            patterns: list of pattern strings in format "field:'regex'"
        """
        self.patterns: list[FilterPattern] = []
        if patterns:
            for pattern in patterns:
                self.add_pattern(pattern)

    def add_pattern(self, pattern_str: str) -> None:
        """Add a filter pattern.

        Args:
            pattern_str: Pattern string in format "field:'regex'"

        Raises:
            ValueError: If pattern format is invalid
        """
        pattern = self._parse_pattern(pattern_str)
        self.patterns.append(pattern)

    def _parse_pattern(self, pattern_str: str) -> FilterPattern:
        """Parse a pattern string into a FilterPattern.

        Args:
            pattern_str: Pattern string like "artist:'.*dead.*'", "track:'.*signal.*'", or "album:'.*1989.*'"

        Returns:
            FilterPattern object

        Raises:
            ValueError: If pattern format is invalid
        """
        # Pattern format: field:'regex'
        pattern_str = pattern_str.strip()

        # Match field:'regex' format
        match = re.match(r"^(artist|track|album):'(.+)'$", pattern_str, re.IGNORECASE)
        if not match:
            raise ValueError(
                f"Invalid pattern format: {pattern_str}. Expected format: field:'regex'"
            )

        field = match.group(1).lower()
        regex_str = match.group(2)

        try:
            compiled_regex = re.compile(regex_str, re.IGNORECASE)
        except re.error as e:
            raise ValueError(f"Invalid regex pattern '{regex_str}': {e}")

        return FilterPattern(field=field, pattern=regex_str, regex=compiled_regex)

    def matches(self, item: Track) -> bool:
        """Check if a track matches any of the filter patterns.

        Args:
            item: Track to check

        Returns:
            True if track matches any pattern, False if no patterns or no matches
        """
        if not self.patterns:
            # No patterns means no filtering (include all)
            return False

        # Check each pattern - OR logic (any match filters the item)
        for pattern in self.patterns:
            if pattern.field == "artist":
                if pattern.regex.search(item.artist):
                    return True
            elif pattern.field == "track":
                if pattern.regex.search(item.title):
                    return True
            elif pattern.field == "album":
                # Album is optional, only check if present
                if item.album and pattern.regex.search(item.album):
                    return True

        # No patterns matched
        return False

    @classmethod
    def from_string(cls, filter_string: str) -> "TrackFilter":
        """Create filter from comma-separated pattern string.

        Args:
            filter_string: String like "artist:'.*dead.*',track:'.*signal.*'"

        Returns:
            TrackFilter instance
        """
        if not filter_string.strip():
            return cls()

        patterns = []
        # Reuse AlbumFilter's split logic
        pattern_parts = AlbumFilter._split_filter_string(filter_string)

        for part in pattern_parts:
            part = part.strip()
            if part:
                patterns.append(part)

        return cls(patterns)

    @classmethod
    def from_file(cls, file_path: str) -> "TrackFilter":
        """Create filter from file containing patterns.

        Args:
            file_path: Path to file with one pattern per line

        Returns:
            TrackFilter instance

        Raises:
            FileNotFoundError: If file doesn't exist
            ValueError: If file contains invalid patterns
        """
        if not os.path.exists(file_path):
            raise FileNotFoundError(f"Filter file not found: {file_path}")

        patterns = []
        with open(file_path, "r", encoding="utf-8") as f:
            for line_num, line in enumerate(f, 1):
                line = line.strip()
                # Skip empty lines and comments
                if not line or line.startswith("#"):
                    continue

                try:
                    # Each line can contain multiple comma-separated patterns
                    line_patterns = AlbumFilter._split_filter_string(line)
                    for pattern in line_patterns:
                        pattern = pattern.strip()
                        if pattern:
                            patterns.append(pattern)
                except ValueError as e:
                    raise ValueError(f"Invalid pattern on line {line_num}: {e}")

        return cls(patterns)

    def get_summary(self) -> str:
        """Get a human-readable summary of the filter patterns.

        Returns:
            Summary string
        """
        if not self.patterns:
            return "No filters (all tracks included)"

        summary_parts = []
        artist_patterns = []
        track_patterns = []
        album_patterns = []

        for pattern in self.patterns:
            if pattern.field == "artist":
                artist_patterns.append(pattern.pattern)
            elif pattern.field == "track":
                track_patterns.append(pattern.pattern)
            elif pattern.field == "album":
                album_patterns.append(pattern.pattern)

        if artist_patterns:
            summary_parts.append(f"Artist patterns: {', '.join(artist_patterns)}")
        if track_patterns:
            summary_parts.append(f"Track patterns: {', '.join(track_patterns)}")
        if album_patterns:
            summary_parts.append(f"Album patterns: {', '.join(album_patterns)}")

        return "; ".join(summary_parts)

    def __len__(self) -> int:
        """Return number of patterns."""
        return len(self.patterns)

    def __bool__(self) -> bool:
        """Return True if filter has patterns."""
        return bool(self.patterns)
