"""
Import/Export functionality for hosts entries.

This module provides comprehensive import/export capabilities for multiple
file formats including hosts, JSON, and CSV with validation and error handling.
"""

import json
import csv
from pathlib import Path
from typing import List, Optional
from dataclasses import dataclass
from enum import Enum
from datetime import datetime

from .models import HostEntry, HostsFile

class ExportFormat(Enum):
    """Supported export formats."""
    HOSTS = "hosts"
    JSON = "json"
    CSV = "csv"

class ImportFormat(Enum):
    """Supported import formats."""
    HOSTS = "hosts"
    JSON = "json"
    CSV = "csv"

@dataclass
class ImportResult:
    """Result of an import operation."""
    success: bool
    entries: List[HostEntry]
    errors: List[str]
    warnings: List[str]
    total_processed: int
    successfully_imported: int
    
    @property
    def has_errors(self) -> bool:
        """Check if import had any errors."""
        return len(self.errors) > 0
    
    @property
    def has_warnings(self) -> bool:
        """Check if import had any warnings."""
        return len(self.warnings) > 0

@dataclass
class ExportResult:
    """Result of an export operation."""
    success: bool
    file_path: Path
    entries_exported: int
    errors: List[str]
    format: ExportFormat

class ImportExportService:
    """Handle multiple file format operations for hosts entries."""
    
    def __init__(self):
        """Initialize the import/export service."""
        self.supported_export_formats = [ExportFormat.HOSTS, ExportFormat.JSON, ExportFormat.CSV]
        self.supported_import_formats = [ImportFormat.HOSTS, ImportFormat.JSON, ImportFormat.CSV]
    
    # Export Methods
    
    def export_hosts_format(self, hosts_file: HostsFile, path: Path) -> ExportResult:
        """
        Export hosts file to standard hosts format.
        
        Args:
            hosts_file: HostsFile instance to export
            path: Path where to save the exported file
            
        Returns:
            ExportResult with operation details
        """
        try:
            from .parser import HostsParser
            
            # Use the parser to serialize and write the hosts file
            parser = HostsParser(str(path))
            content = parser.serialize(hosts_file)
            
            # Write the content to file
            with open(path, 'w', encoding='utf-8') as f:
                f.write(content)
            
            return ExportResult(
                success=True,
                file_path=path,
                entries_exported=len(hosts_file.entries),
                errors=[],
                format=ExportFormat.HOSTS
            )
        except Exception as e:
            return ExportResult(
                success=False,
                file_path=path,
                entries_exported=0,
                errors=[f"Failed to export hosts format: {str(e)}"],
                format=ExportFormat.HOSTS
            )
    
    def export_json_format(self, hosts_file: HostsFile, path: Path) -> ExportResult:
        """
        Export hosts file to JSON format with metadata.
        
        Args:
            hosts_file: HostsFile instance to export
            path: Path where to save the exported file
            
        Returns:
            ExportResult with operation details
        """
        try:
            export_data = {
                "metadata": {
                    "exported_at": datetime.now().isoformat(),
                    "total_entries": len(hosts_file.entries),
                    "version": "1.0",
                    "format": "hosts_json_export"
                },
                "entries": []
            }
            
            for entry in hosts_file.entries:
                entry_data = {
                    "ip_address": entry.ip_address,
                    "hostnames": entry.hostnames,
                    "comment": entry.comment,
                    "is_active": entry.is_active
                }
                
                # Add DNS fields if present
                if entry.dns_name:
                    entry_data["dns_name"] = entry.dns_name
                if entry.resolved_ip:
                    entry_data["resolved_ip"] = entry.resolved_ip
                if entry.last_resolved:
                    entry_data["last_resolved"] = entry.last_resolved.isoformat()
                if entry.dns_resolution_status:
                    entry_data["dns_resolution_status"] = entry.dns_resolution_status
                
                export_data["entries"].append(entry_data)
            
            with open(path, 'w', encoding='utf-8') as f:
                json.dump(export_data, f, indent=2, ensure_ascii=False)
            
            return ExportResult(
                success=True,
                file_path=path,
                entries_exported=len(hosts_file.entries),
                errors=[],
                format=ExportFormat.JSON
            )
            
        except Exception as e:
            return ExportResult(
                success=False,
                file_path=path,
                entries_exported=0,
                errors=[f"Failed to export JSON format: {str(e)}"],
                format=ExportFormat.JSON
            )
    
    def export_csv_format(self, hosts_file: HostsFile, path: Path) -> ExportResult:
        """
        Export hosts file to CSV format.
        
        Args:
            hosts_file: HostsFile instance to export
            path: Path where to save the exported file
            
        Returns:
            ExportResult with operation details
        """
        try:
            fieldnames = [
                'ip_address', 'hostnames', 'comment', 'is_active',
                'dns_name', 'resolved_ip', 'last_resolved', 'dns_resolution_status'
            ]
            
            with open(path, 'w', newline='', encoding='utf-8') as csvfile:
                writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
                writer.writeheader()
                
                for entry in hosts_file.entries:
                    row_data = {
                        'ip_address': entry.ip_address,
                        'hostnames': ' '.join(entry.hostnames),
                        'comment': entry.comment or '',
                        'is_active': entry.is_active,
                        'dns_name': entry.dns_name or '',
                        'resolved_ip': entry.resolved_ip or '',
                        'last_resolved': entry.last_resolved.isoformat() if entry.last_resolved else '',
                        'dns_resolution_status': entry.dns_resolution_status or ''
                    }
                    writer.writerow(row_data)
            
            return ExportResult(
                success=True,
                file_path=path,
                entries_exported=len(hosts_file.entries),
                errors=[],
                format=ExportFormat.CSV
            )
            
        except Exception as e:
            return ExportResult(
                success=False,
                file_path=path,
                entries_exported=0,
                errors=[f"Failed to export CSV format: {str(e)}"],
                format=ExportFormat.CSV
            )
    
    # Import Methods
    
    def import_hosts_format(self, path: Path) -> ImportResult:
        """
        Import from hosts file format.
        
        Args:
            path: Path to the hosts file to import
            
        Returns:
            ImportResult with imported entries and any errors
        """
        try:
            from .parser import HostsParser
            
            parser = HostsParser(str(path))
            hosts_file = parser.parse()
            
            return ImportResult(
                success=True,
                entries=hosts_file.entries,
                errors=[],
                warnings=[],
                total_processed=len(hosts_file.entries),
                successfully_imported=len(hosts_file.entries)
            )
            
        except Exception as e:
            return ImportResult(
                success=False,
                entries=[],
                errors=[f"Failed to import hosts format: {str(e)}"],
                warnings=[],
                total_processed=0,
                successfully_imported=0
            )
    
    def import_json_format(self, path: Path) -> ImportResult:
        """
        Import from JSON format with validation.
        
        Args:
            path: Path to the JSON file to import
            
        Returns:
            ImportResult with imported entries and any errors
        """
        try:
            with open(path, 'r', encoding='utf-8') as f:
                data = json.load(f)
            
            if not isinstance(data, dict) or 'entries' not in data:
                return ImportResult(
                    success=False,
                    entries=[],
                    errors=["Invalid JSON format: missing 'entries' field"],
                    warnings=[],
                    total_processed=0,
                    successfully_imported=0
                )
            
            entries = []
            errors = []
            warnings = []
            total_processed = len(data['entries'])
            
            for i, entry_data in enumerate(data['entries']):
                try:
                    # Validate required fields
                    if not isinstance(entry_data, dict):
                        errors.append(f"Entry {i+1}: Invalid entry format")
                        continue
                    
                    if 'hostnames' not in entry_data or not entry_data['hostnames']:
                        errors.append(f"Entry {i+1}: Missing hostnames field")
                        continue
                    
                    # Handle DNS vs IP entries
                    dns_name = entry_data.get('dns_name', '')
                    ip_address = entry_data.get('ip_address', '')
                    
                    # Create entry with temporary IP if it's a DNS-only entry
                    if dns_name and not ip_address:
                        # Create with temporary IP, then convert to DNS entry
                        entry = HostEntry(
                            ip_address="127.0.0.1",  # Temporary IP
                            hostnames=entry_data['hostnames'],
                            comment=entry_data.get('comment', ''),
                            is_active=entry_data.get('is_active', True)
                        )
                        # Convert to DNS entry
                        entry.ip_address = ""
                        entry.dns_name = dns_name
                    else:
                        # Regular IP entry
                        entry = HostEntry(
                            ip_address=ip_address,
                            hostnames=entry_data['hostnames'],
                            comment=entry_data.get('comment', ''),
                            is_active=entry_data.get('is_active', True)
                        )
                        # Set DNS name if present for IP entries
                        if dns_name:
                            entry.dns_name = dns_name
                    if 'resolved_ip' in entry_data:
                        entry.resolved_ip = entry_data['resolved_ip']
                    if 'last_resolved' in entry_data and entry_data['last_resolved']:
                        try:
                            entry.last_resolved = datetime.fromisoformat(entry_data['last_resolved'])
                        except ValueError:
                            warnings.append(f"Entry {i+1}: Invalid last_resolved date format")
                    if 'dns_resolution_status' in entry_data:
                        entry.dns_resolution_status = entry_data['dns_resolution_status']
                    
                    entries.append(entry)
                    
                except ValueError as e:
                    errors.append(f"Entry {i+1}: {str(e)}")
                except Exception as e:
                    errors.append(f"Entry {i+1}: Unexpected error - {str(e)}")
            
            return ImportResult(
                success=len(errors) == 0,
                entries=entries,
                errors=errors,
                warnings=warnings,
                total_processed=total_processed,
                successfully_imported=len(entries)
            )
            
        except json.JSONDecodeError as e:
            return ImportResult(
                success=False,
                entries=[],
                errors=[f"Invalid JSON file: {str(e)}"],
                warnings=[],
                total_processed=0,
                successfully_imported=0
            )
        except Exception as e:
            return ImportResult(
                success=False,
                entries=[],
                errors=[f"Failed to import JSON format: {str(e)}"],
                warnings=[],
                total_processed=0,
                successfully_imported=0
            )
    
    def import_csv_format(self, path: Path) -> ImportResult:
        """
        Import from CSV format with field mapping.
        
        Args:
            path: Path to the CSV file to import
            
        Returns:
            ImportResult with imported entries and any errors
        """
        try:
            entries = []
            errors = []
            warnings = []
            total_processed = 0
            
            with open(path, 'r', encoding='utf-8') as csvfile:
                # Try to detect the dialect
                sample = csvfile.read(1024)
                csvfile.seek(0)
                dialect = csv.Sniffer().sniff(sample)
                
                reader = csv.DictReader(csvfile, dialect=dialect)
                
                # Validate required columns
                required_columns = ['hostnames']
                missing_columns = [col for col in required_columns if col not in reader.fieldnames]
                if missing_columns:
                    return ImportResult(
                        success=False,
                        entries=[],
                        errors=[f"Missing required columns: {missing_columns}"],
                        warnings=[],
                        total_processed=0,
                        successfully_imported=0
                    )
                
                for row_num, row in enumerate(reader, start=2):  # Start at 2 for header
                    total_processed += 1
                    try:
                        # Parse hostnames
                        hostnames_str = row.get('hostnames', '').strip()
                        if not hostnames_str:
                            errors.append(f"Row {row_num}: Empty hostnames field")
                            continue
                        
                        hostnames = [h.strip() for h in hostnames_str.split()]
                        if not hostnames:
                            errors.append(f"Row {row_num}: No valid hostnames found")
                            continue
                        
                        # Parse is_active
                        is_active_str = row.get('is_active', 'true').lower()
                        is_active = is_active_str in ('true', '1', 'yes', 'active')
                        
                        # Handle DNS vs IP entries
                        dns_name = row.get('dns_name', '').strip()
                        ip_address = row.get('ip_address', '').strip()
                        
                        # Create entry with temporary IP if it's a DNS-only entry
                        if dns_name and not ip_address:
                            # Create with temporary IP, then convert to DNS entry
                            entry = HostEntry(
                                ip_address="127.0.0.1",  # Temporary IP
                                hostnames=hostnames,
                                comment=row.get('comment', '').strip(),
                                is_active=is_active
                            )
                            # Convert to DNS entry
                            entry.ip_address = ""
                            entry.dns_name = dns_name
                        else:
                            # Regular IP entry
                            entry = HostEntry(
                                ip_address=ip_address,
                                hostnames=hostnames,
                                comment=row.get('comment', '').strip(),
                                is_active=is_active
                            )
                            # Set DNS name if present for IP entries
                            if dns_name:
                                entry.dns_name = dns_name
                        if row.get('resolved_ip', '').strip():
                            entry.resolved_ip = row['resolved_ip'].strip()
                        if row.get('last_resolved', '').strip():
                            try:
                                entry.last_resolved = datetime.fromisoformat(row['last_resolved'].strip())
                            except ValueError:
                                warnings.append(f"Row {row_num}: Invalid last_resolved date format")
                        if row.get('dns_resolution_status', '').strip():
                            entry.dns_resolution_status = row['dns_resolution_status'].strip()
                        
                        entries.append(entry)
                        
                    except ValueError as e:
                        errors.append(f"Row {row_num}: {str(e)}")
                    except Exception as e:
                        errors.append(f"Row {row_num}: Unexpected error - {str(e)}")
            
            return ImportResult(
                success=len(errors) == 0,
                entries=entries,
                errors=errors,
                warnings=warnings,
                total_processed=total_processed,
                successfully_imported=len(entries)
            )
            
        except Exception as e:
            return ImportResult(
                success=False,
                entries=[],
                errors=[f"Failed to import CSV format: {str(e)}"],
                warnings=[],
                total_processed=0,
                successfully_imported=0
            )
    
    # Utility Methods
    
    def detect_file_format(self, path: Path) -> Optional[ImportFormat]:
        """
        Detect the format of a file based on extension and content.
        
        Args:
            path: Path to the file to analyze
            
        Returns:
            Detected ImportFormat or None if unknown
        """
        if not path.exists():
            return None
        
        # Check by extension first
        extension = path.suffix.lower()
        if extension == '.json':
            return ImportFormat.JSON
        elif extension == '.csv':
            return ImportFormat.CSV
        elif path.name in ['hosts', '/etc/hosts'] or extension in ['.hosts', '.txt']:
            return ImportFormat.HOSTS
        
        # Try to detect by content
        try:
            with open(path, 'r', encoding='utf-8') as f:
                first_line = f.readline().strip()
                
                # Check for JSON
                if first_line.startswith('{'):
                    return ImportFormat.JSON
                
                # Check for CSV (look for comma separators)
                if ',' in first_line and not first_line.startswith('#'):
                    return ImportFormat.CSV
                
                # Default to hosts format
                return ImportFormat.HOSTS
                
        except Exception:
            return None
    
    def validate_export_path(self, path: Path, format: ExportFormat) -> List[str]:
        """
        Validate export path and return any warnings.
        
        Args:
            path: Target export path
            format: Export format
            
        Returns:
            List of validation warnings
        """
        warnings = []
        
        # Check if file already exists
        if path.exists():
            warnings.append(f"File {path} already exists and will be overwritten")
        
        # Check if directory exists
        if not path.parent.exists():
            warnings.append(f"Directory {path.parent} does not exist")
        
        # Check write permissions
        try:
            path.parent.mkdir(parents=True, exist_ok=True)
            test_file = path.parent / '.write_test'
            test_file.touch()
            test_file.unlink()
        except Exception:
            warnings.append(f"No write permission for directory {path.parent}")
        
        # Check extension matches format
        expected_extensions = {
            ExportFormat.HOSTS: ['.hosts', '.txt', ''],
            ExportFormat.JSON: ['.json'],
            ExportFormat.CSV: ['.csv']
        }
        
        if path.suffix.lower() not in expected_extensions[format]:
            suggested_ext = expected_extensions[format][0] if expected_extensions[format] else ''
            warnings.append(f"File extension '{path.suffix}' doesn't match format {format.value}{f', suggest {suggested_ext}' if suggested_ext else ''}")
        
        return warnings
    
    def get_supported_export_formats(self) -> List[ExportFormat]:
        """Get list of supported export formats."""
        return self.supported_export_formats.copy()
    
    def get_supported_import_formats(self) -> List[ImportFormat]:
        """Get list of supported import formats."""
        return self.supported_import_formats.copy()
