"""
Advanced filtering system for hosts entries.

This module provides comprehensive filtering capabilities including status-based,
type-based, and DNS resolution-based filtering with preset management.
"""

from dataclasses import dataclass
from typing import List, Optional, Dict, Any
from enum import Enum

from .models import HostEntry


class FilterType(Enum):
    """Filter type enumeration."""
    STATUS = "status"
    DNS_TYPE = "dns_type"
    RESOLUTION_STATUS = "resolution_status"
    SEARCH = "search"


@dataclass
class FilterOptions:
    """Configuration options for filtering entries."""
    # Status filtering
    show_active: bool = True
    show_inactive: bool = True
    active_only: bool = False
    inactive_only: bool = False
    
    # DNS type filtering
    show_dns_entries: bool = True
    show_ip_entries: bool = True
    dns_only: bool = False
    ip_only: bool = False
    
    # DNS resolution status filtering
    show_resolved: bool = True
    show_unresolved: bool = True
    show_resolving: bool = True
    show_failed: bool = True
    show_mismatched: bool = True
    mismatch_only: bool = False
    resolved_only: bool = False
    
    # Search filtering
    search_term: Optional[str] = None
    search_in_hostnames: bool = True
    search_in_comments: bool = True
    search_in_ips: bool = True
    case_sensitive: bool = False
    
    # Filter preset
    preset_name: Optional[str] = None

    def to_dict(self) -> Dict[str, Any]:
        """Convert FilterOptions to dictionary."""
        return {
            'show_active': self.show_active,
            'show_inactive': self.show_inactive,
            'active_only': self.active_only,
            'inactive_only': self.inactive_only,
            'show_dns_entries': self.show_dns_entries,
            'show_ip_entries': self.show_ip_entries,
            'dns_only': self.dns_only,
            'ip_only': self.ip_only,
            'show_resolved': self.show_resolved,
            'show_unresolved': self.show_unresolved,
            'show_resolving': self.show_resolving,
            'show_failed': self.show_failed,
            'show_mismatched': self.show_mismatched,
            'mismatch_only': self.mismatch_only,
            'resolved_only': self.resolved_only,
            'search_term': self.search_term or "",
            'search_in_hostnames': self.search_in_hostnames,
            'search_in_comments': self.search_in_comments,
            'search_in_ips': self.search_in_ips,
            'case_sensitive': self.case_sensitive,
            'preset_name': self.preset_name
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> 'FilterOptions':
        """Create FilterOptions from dictionary."""
        return cls(
            show_active=data.get('show_active', True),
            show_inactive=data.get('show_inactive', True),
            active_only=data.get('active_only', False),
            inactive_only=data.get('inactive_only', False),
            show_dns_entries=data.get('show_dns_entries', True),
            show_ip_entries=data.get('show_ip_entries', True),
            dns_only=data.get('dns_only', False),
            ip_only=data.get('ip_only', False),
            show_resolved=data.get('show_resolved', True),
            show_unresolved=data.get('show_unresolved', True),
            show_resolving=data.get('show_resolving', True),
            show_failed=data.get('show_failed', True),
            show_mismatched=data.get('show_mismatched', True),
            mismatch_only=data.get('mismatch_only', False),
            resolved_only=data.get('resolved_only', False),
            search_term=data.get('search_term', None),
            search_in_hostnames=data.get('search_in_hostnames', True),
            search_in_comments=data.get('search_in_comments', True),
            search_in_ips=data.get('search_in_ips', True),
            case_sensitive=data.get('case_sensitive', False),
            preset_name=data.get('preset_name', None)
        )

    def is_empty(self) -> bool:
        """Check if filter options represent no filtering (default state)."""
        return (
            self.show_active and self.show_inactive and
            not self.active_only and not self.inactive_only and
            self.show_dns_entries and self.show_ip_entries and
            not self.dns_only and not self.ip_only and
            self.show_resolved and self.show_unresolved and
            self.show_resolving and self.show_failed and self.show_mismatched and
            not self.mismatch_only and not self.resolved_only and
            not self.search_term
        )


class EntryFilter:
    """Advanced filtering logic for hosts entries."""
    
    def __init__(self):
        """Initialize the entry filter."""
        self.presets: Dict[str, FilterOptions] = {}
        self._load_default_presets()
    
    def _load_default_presets(self) -> None:
        """Load default filter presets."""
        self.presets = {
            "All Entries": FilterOptions(),
            "Active Only": FilterOptions(
                show_inactive=False,
                active_only=True
            ),
            "Inactive Only": FilterOptions(
                show_active=False,
                inactive_only=True
            ),
            "DNS Entries Only": FilterOptions(
                show_ip_entries=False,
                dns_only=True
            ),
            "IP Entries Only": FilterOptions(
                show_dns_entries=False,
                ip_only=True
            ),
            "DNS Mismatches": FilterOptions(
                mismatch_only=True
            ),
            "Resolution Failed": FilterOptions(
                show_resolved=False,
                show_unresolved=False,
                show_resolving=False,
                show_mismatched=False
            ),
            "Needs Resolution": FilterOptions(
                show_resolved=False,
                show_failed=False,
                show_mismatched=False
            )
        }
    
    def apply_filters(self, entries: List[HostEntry], options: FilterOptions) -> List[HostEntry]:
        """
        Apply all filter criteria to the list of entries.
        
        Args:
            entries: List of host entries to filter
            options: Filter configuration options
            
        Returns:
            Filtered list of entries
        """
        filtered_entries = entries.copy()
        
        # Apply status filtering
        if options.active_only or options.inactive_only or not (options.show_active and options.show_inactive):
            filtered_entries = self.filter_by_status(filtered_entries, options)
        
        # Apply DNS type filtering
        if options.dns_only or options.ip_only or not (options.show_dns_entries and options.show_ip_entries):
            filtered_entries = self.filter_by_dns_type(filtered_entries, options)
        
        # Apply DNS resolution status filtering
        if options.mismatch_only or options.resolved_only or not self._all_resolution_status_shown(options):
            filtered_entries = self.filter_by_resolution_status(filtered_entries, options)
        
        # Apply search filtering
        if options.search_term:
            filtered_entries = self.filter_by_search(filtered_entries, options)
        
        return filtered_entries
    
    def filter_by_status(self, entries: List[HostEntry], options: FilterOptions) -> List[HostEntry]:
        """
        Filter entries by active/inactive status.
        
        Args:
            entries: List of entries to filter
            options: Filter options containing status criteria
            
        Returns:
            Filtered list of entries
        """
        if options.active_only:
            return [entry for entry in entries if entry.is_active]
        elif options.inactive_only:
            return [entry for entry in entries if not entry.is_active]
        else:
            # Show based on individual flags
            filtered = []
            for entry in entries:
                if entry.is_active and options.show_active:
                    filtered.append(entry)
                elif not entry.is_active and options.show_inactive:
                    filtered.append(entry)
            return filtered
    
    def filter_by_dns_type(self, entries: List[HostEntry], options: FilterOptions) -> List[HostEntry]:
        """
        Filter entries by DNS name vs IP address type.
        
        Args:
            entries: List of entries to filter
            options: Filter options containing DNS type criteria
            
        Returns:
            Filtered list of entries
        """
        if options.dns_only:
            return [entry for entry in entries if entry.has_dns_name()]
        elif options.ip_only:
            return [entry for entry in entries if not entry.has_dns_name()]
        else:
            # Show based on individual flags
            filtered = []
            for entry in entries:
                if entry.has_dns_name() and options.show_dns_entries:
                    filtered.append(entry)
                elif not entry.has_dns_name() and options.show_ip_entries:
                    filtered.append(entry)
            return filtered
    
    def filter_by_resolution_status(self, entries: List[HostEntry], options: FilterOptions) -> List[HostEntry]:
        """
        Filter entries by DNS resolution status.
        
        Args:
            entries: List of entries to filter
            options: Filter options containing resolution status criteria
            
        Returns:
            Filtered list of entries
        """
        if options.mismatch_only:
            return [entry for entry in entries 
                   if entry.dns_resolution_status == "IP_MISMATCH"]
        elif options.resolved_only:
            return [entry for entry in entries 
                   if entry.dns_resolution_status in ["IP_MATCH", "RESOLVED"]]
        else:
            # Show based on individual flags
            filtered = []
            for entry in entries:
                status = entry.dns_resolution_status or "NOT_RESOLVED"
                
                if (status == "NOT_RESOLVED" and options.show_unresolved) or \
                   (status == "RESOLVING" and options.show_resolving) or \
                   (status in ["IP_MATCH", "RESOLVED"] and options.show_resolved) or \
                   (status == "RESOLUTION_FAILED" and options.show_failed) or \
                   (status == "IP_MISMATCH" and options.show_mismatched):
                    filtered.append(entry)
            
            return filtered
    
    def filter_by_search(self, entries: List[HostEntry], options: FilterOptions) -> List[HostEntry]:
        """
        Filter entries by search term.
        
        Args:
            entries: List of entries to filter
            options: Filter options containing search criteria
            
        Returns:
            Filtered list of entries
        """
        if not options.search_term:
            return entries
        
        search_term = options.search_term
        if not options.case_sensitive:
            search_term = search_term.lower()
        
        filtered = []
        for entry in entries:
            match_found = False
            
            # Search in hostnames
            if options.search_in_hostnames:
                hostnames_text = " ".join(entry.hostnames)
                if not options.case_sensitive:
                    hostnames_text = hostnames_text.lower()
                if search_term in hostnames_text:
                    match_found = True
            
            # Search in comments
            if not match_found and options.search_in_comments and entry.comment:
                comment_text = entry.comment
                if not options.case_sensitive:
                    comment_text = comment_text.lower()
                if search_term in comment_text:
                    match_found = True
            
            # Search in IP addresses
            if not match_found and options.search_in_ips:
                ip_text = entry.ip_address or ""
                if entry.resolved_ip:
                    ip_text += f" {entry.resolved_ip}"
                if not options.case_sensitive:
                    ip_text = ip_text.lower()
                if search_term in ip_text:
                    match_found = True
            
            if match_found:
                filtered.append(entry)
        
        return filtered
    
    def _all_resolution_status_shown(self, options: FilterOptions) -> bool:
        """Check if all resolution status types are shown."""
        return (options.show_resolved and options.show_unresolved and 
                options.show_resolving and options.show_failed and 
                options.show_mismatched)
    
    def save_preset(self, name: str, options: FilterOptions) -> None:
        """
        Save filter options as a preset.
        
        Args:
            name: Name for the preset
            options: Filter options to save
        """
        preset_options = FilterOptions(
            show_active=options.show_active,
            show_inactive=options.show_inactive,
            active_only=options.active_only,
            inactive_only=options.inactive_only,
            show_dns_entries=options.show_dns_entries,
            show_ip_entries=options.show_ip_entries,
            dns_only=options.dns_only,
            ip_only=options.ip_only,
            show_resolved=options.show_resolved,
            show_unresolved=options.show_unresolved,
            show_resolving=options.show_resolving,
            show_failed=options.show_failed,
            show_mismatched=options.show_mismatched,
            mismatch_only=options.mismatch_only,
            resolved_only=options.resolved_only,
            # Don't save search terms in presets
            search_term=None,
            search_in_hostnames=options.search_in_hostnames,
            search_in_comments=options.search_in_comments,
            search_in_ips=options.search_in_ips,
            case_sensitive=options.case_sensitive,
            preset_name=name
        )
        self.presets[name] = preset_options
    
    def load_preset(self, name: str) -> Optional[FilterOptions]:
        """
        Load filter options from a preset.
        
        Args:
            name: Name of the preset to load
            
        Returns:
            Filter options if preset exists, None otherwise
        """
        return self.presets.get(name)
    
    def delete_preset(self, name: str) -> bool:
        """
        Delete a preset.
        
        Args:
            name: Name of the preset to delete
            
        Returns:
            True if preset was deleted, False if it didn't exist
        """
        if name in self.presets:
            del self.presets[name]
            return True
        return False
    
    def get_preset_names(self) -> List[str]:
        """
        Get list of available preset names.
        
        Returns:
            List of preset names
        """
        return list(self.presets.keys())
    
    def get_default_presets(self) -> Dict[str, FilterOptions]:
        """
        Get the default filter presets.
        
        Returns:
            Dictionary of default presets
        """
        return {
            "All Entries": FilterOptions(),
            "Active Only": FilterOptions(
                show_inactive=False,
                active_only=True
            ),
            "Inactive Only": FilterOptions(
                show_active=False,
                inactive_only=True
            ),
            "DNS Entries Only": FilterOptions(
                show_ip_entries=False,
                dns_only=True
            ),
            "IP Entries Only": FilterOptions(
                show_dns_entries=False,
                ip_only=True
            ),
            "DNS Mismatches": FilterOptions(
                mismatch_only=True
            ),
            "Resolved Entries": FilterOptions(
                resolved_only=True
            ),
            "Unresolved Entries": FilterOptions(
                show_resolved=False,
                show_resolving=False,
                show_failed=False,
                show_mismatched=False
            )
        }
    
    def get_saved_presets(self) -> Dict[str, FilterOptions]:
        """
        Get all saved presets (both default and custom).
        
        Returns:
            Dictionary of all presets
        """
        return self.presets.copy()
    
    def count_filtered_entries(self, entries: List[HostEntry], options: FilterOptions) -> Dict[str, int]:
        """
        Count entries by category for the given filter options.
        
        Args:
            entries: List of entries to analyze
            options: Filter options to apply
            
        Returns:
            Dictionary with count statistics
        """
        filtered_entries = self.apply_filters(entries, options)
        total_entries = len(entries)
        filtered_count = len(filtered_entries)
        
        # Count by status
        active_count = len([e for e in filtered_entries if e.is_active])
        inactive_count = filtered_count - active_count
        
        # Count by type
        dns_count = len([e for e in filtered_entries if e.has_dns_name()])
        ip_count = filtered_count - dns_count
        
        # Count by resolution status
        resolved_count = len([e for e in filtered_entries 
                            if e.dns_resolution_status in ["IP_MATCH", "RESOLVED"]])
        unresolved_count = len([e for e in filtered_entries 
                              if e.dns_resolution_status in [None, "NOT_RESOLVED"]])
        resolving_count = len([e for e in filtered_entries 
                             if e.dns_resolution_status == "RESOLVING"])
        failed_count = len([e for e in filtered_entries 
                          if e.dns_resolution_status == "RESOLUTION_FAILED"])
        mismatch_count = len([e for e in filtered_entries 
                            if e.dns_resolution_status == "IP_MISMATCH"])
        
        return {
            "total": total_entries,
            "filtered": filtered_count,
            "active": active_count,
            "inactive": inactive_count,
            "dns_entries": dns_count,
            "ip_entries": ip_count,
            "resolved": resolved_count,
            "unresolved": unresolved_count,
            "resolving": resolving_count,
            "failed": failed_count,
            "mismatched": mismatch_count
        }
