"""
Tests for the import/export functionality.

This module contains comprehensive tests for the ImportExportService class
and all supported file formats.
"""

import pytest
import json
import csv
import tempfile
from pathlib import Path
from datetime import datetime
from src.hosts.core.import_export import (
    ImportExportService, ImportResult, ExportFormat, ImportFormat
)
from src.hosts.core.models import HostEntry, HostsFile

class TestImportExportService:
    """Test ImportExportService class."""

    @pytest.fixture
    def service(self):
        """Create ImportExportService instance."""
        return ImportExportService()

    @pytest.fixture
    def sample_hosts_file(self):
        """Create sample HostsFile for testing."""
        entries = [
            HostEntry("127.0.0.1", ["localhost"], "Local host", True),
            HostEntry("192.168.1.1", ["router.local"], "Home router", True),
            HostEntry("1.1.1.1", ["dns-only.com"], "DNS only entry", False),  # Temp IP
            HostEntry("10.0.0.1", ["test.example.com"], "Test server", True)
        ]
        
        # Convert to DNS entry and set DNS data for some entries
        entries[2].ip_address = ""  # Remove IP after creation
        entries[2].dns_name = "dns-only.com"
        entries[3].resolved_ip = "10.0.0.1"
        entries[3].last_resolved = datetime(2024, 1, 15, 12, 0, 0)
        entries[3].dns_resolution_status = "IP_MATCH"
        
        hosts_file = HostsFile()
        hosts_file.entries = entries
        return hosts_file

    @pytest.fixture
    def temp_dir(self):
        """Create temporary directory for test files."""
        with tempfile.TemporaryDirectory() as tmpdir:
            yield Path(tmpdir)

    def test_service_initialization(self, service):
        """Test service initialization."""
        assert len(service.supported_export_formats) == 3
        assert len(service.supported_import_formats) == 3
        assert ExportFormat.HOSTS in service.supported_export_formats
        assert ExportFormat.JSON in service.supported_export_formats
        assert ExportFormat.CSV in service.supported_export_formats

    def test_get_supported_formats(self, service):
        """Test getting supported formats."""
        export_formats = service.get_supported_export_formats()
        import_formats = service.get_supported_import_formats()
        
        assert len(export_formats) == 3
        assert len(import_formats) == 3
        assert ExportFormat.HOSTS in export_formats
        assert ImportFormat.JSON in import_formats

    # Export Tests

    def test_export_hosts_format(self, service, sample_hosts_file, temp_dir):
        """Test exporting to hosts format."""
        export_path = temp_dir / "test_hosts.txt"
        
        result = service.export_hosts_format(sample_hosts_file, export_path)
        
        assert result.success is True
        assert result.entries_exported == 4
        assert len(result.errors) == 0
        assert result.format == ExportFormat.HOSTS
        assert export_path.exists()
        
        # Verify content
        content = export_path.read_text()
        assert "127.0.0.1" in content
        assert "localhost" in content
        assert "router.local" in content

    def test_export_json_format(self, service, sample_hosts_file, temp_dir):
        """Test exporting to JSON format."""
        export_path = temp_dir / "test_export.json"
        
        result = service.export_json_format(sample_hosts_file, export_path)
        
        assert result.success is True
        assert result.entries_exported == 4
        assert len(result.errors) == 0
        assert result.format == ExportFormat.JSON
        assert export_path.exists()
        
        # Verify JSON structure
        with open(export_path, 'r') as f:
            data = json.load(f)
        
        assert "metadata" in data
        assert "entries" in data
        assert data["metadata"]["total_entries"] == 4
        assert len(data["entries"]) == 4
        
        # Check first entry
        first_entry = data["entries"][0]
        assert first_entry["ip_address"] == "127.0.0.1"
        assert first_entry["hostnames"] == ["localhost"]
        assert first_entry["is_active"] is True
        
        # Check DNS entry
        dns_entry = next((e for e in data["entries"] if e.get("dns_name")), None)
        assert dns_entry is not None
        assert dns_entry["dns_name"] == "dns-only.com"

    def test_export_csv_format(self, service, sample_hosts_file, temp_dir):
        """Test exporting to CSV format."""
        export_path = temp_dir / "test_export.csv"
        
        result = service.export_csv_format(sample_hosts_file, export_path)
        
        assert result.success is True
        assert result.entries_exported == 4
        assert len(result.errors) == 0
        assert result.format == ExportFormat.CSV
        assert export_path.exists()
        
        # Verify CSV structure
        with open(export_path, 'r') as f:
            reader = csv.DictReader(f)
            rows = list(reader)
        
        assert len(rows) == 4
        
        # Check header
        expected_fields = [
            'ip_address', 'hostnames', 'comment', 'is_active',
            'dns_name', 'resolved_ip', 'last_resolved', 'dns_resolution_status'
        ]
        assert reader.fieldnames == expected_fields
        
        # Check first row
        first_row = rows[0]
        assert first_row["ip_address"] == "127.0.0.1"
        assert first_row["hostnames"] == "localhost"
        assert first_row["is_active"] == "True"

    def test_export_invalid_path(self, service, sample_hosts_file):
        """Test export with invalid path."""
        invalid_path = Path("/invalid/path/test.json")
        
        result = service.export_json_format(sample_hosts_file, invalid_path)
        
        assert result.success is False
        assert result.entries_exported == 0
        assert len(result.errors) > 0
        assert "Failed to export JSON format" in result.errors[0]

    # Import Tests

    def test_import_hosts_format(self, service, temp_dir):
        """Test importing from hosts format."""
        # Create test hosts file
        hosts_content = """# Test hosts file
127.0.0.1    localhost
192.168.1.1  router.local  # Home router
# 10.0.0.1   disabled.com  # Disabled entry
"""
        hosts_path = temp_dir / "test_hosts.txt"
        hosts_path.write_text(hosts_content)
        
        result = service.import_hosts_format(hosts_path)
        
        assert result.success is True
        assert result.total_processed >= 2
        assert result.successfully_imported >= 2
        assert len(result.errors) == 0
        
        # Check imported entries
        assert len(result.entries) >= 2
        localhost_entry = next((e for e in result.entries if "localhost" in e.hostnames), None)
        assert localhost_entry is not None
        assert localhost_entry.ip_address == "127.0.0.1"
        assert localhost_entry.is_active is True

    def test_import_json_format(self, service, temp_dir):
        """Test importing from JSON format."""
        # Create test JSON file
        json_data = {
            "metadata": {
                "exported_at": "2024-01-15T12:00:00",
                "total_entries": 3,
                "version": "1.0"
            },
            "entries": [
                {
                    "ip_address": "127.0.0.1",
                    "hostnames": ["localhost"],
                    "comment": "Local host",
                    "is_active": True
                },
                {
                    "ip_address": "",
                    "hostnames": ["dns-only.com"],
                    "comment": "DNS only",
                    "is_active": False,
                    "dns_name": "dns-only.com"
                },
                {
                    "ip_address": "10.0.0.1",
                    "hostnames": ["test.com"],
                    "comment": "Test",
                    "is_active": True,
                    "resolved_ip": "10.0.0.1",
                    "last_resolved": "2024-01-15T12:00:00",
                    "dns_resolution_status": "IP_MATCH"
                }
            ]
        }
        
        json_path = temp_dir / "test_import.json"
        with open(json_path, 'w') as f:
            json.dump(json_data, f)
        
        result = service.import_json_format(json_path)
        
        assert result.success is True
        assert result.total_processed == 3
        assert result.successfully_imported == 3
        assert len(result.errors) == 0
        assert len(result.entries) == 3
        
        # Check DNS entry
        dns_entry = next((e for e in result.entries if e.dns_name), None)
        assert dns_entry is not None
        assert dns_entry.dns_name == "dns-only.com"
        assert dns_entry.ip_address == ""
        
        # Check resolved entry
        resolved_entry = next((e for e in result.entries if e.resolved_ip), None)
        assert resolved_entry is not None
        assert resolved_entry.resolved_ip == "10.0.0.1"
        assert resolved_entry.dns_resolution_status == "IP_MATCH"

    def test_import_csv_format(self, service, temp_dir):
        """Test importing from CSV format."""
        # Create test CSV file
        csv_path = temp_dir / "test_import.csv"
        with open(csv_path, 'w', newline='') as f:
            writer = csv.writer(f)
            writer.writerow([
                'ip_address', 'hostnames', 'comment', 'is_active',
                'dns_name', 'resolved_ip', 'last_resolved', 'dns_resolution_status'
            ])
            writer.writerow([
                '127.0.0.1', 'localhost', 'Local host', 'true',
                '', '', '', ''
            ])
            writer.writerow([
                '', 'dns-only.com', 'DNS only', 'false',
                'dns-only.com', '', '', ''
            ])
            writer.writerow([
                '10.0.0.1', 'test.com example.com', 'Test server', 'true',
                '', '10.0.0.1', '2024-01-15T12:00:00', 'IP_MATCH'
            ])
        
        result = service.import_csv_format(csv_path)
        
        assert result.success is True
        assert result.total_processed == 3
        assert result.successfully_imported == 3
        assert len(result.errors) == 0
        assert len(result.entries) == 3
        
        # Check multiple hostnames entry
        multi_hostname_entry = next((e for e in result.entries if "test.com" in e.hostnames), None)
        assert multi_hostname_entry is not None
        assert "example.com" in multi_hostname_entry.hostnames
        assert len(multi_hostname_entry.hostnames) == 2

    def test_import_json_invalid_format(self, service, temp_dir):
        """Test importing invalid JSON format."""
        # Create invalid JSON file
        invalid_json = {"invalid": "format", "no_entries": True}
        json_path = temp_dir / "invalid.json"
        with open(json_path, 'w') as f:
            json.dump(invalid_json, f)
        
        result = service.import_json_format(json_path)
        
        assert result.success is False
        assert result.total_processed == 0
        assert result.successfully_imported == 0
        assert len(result.errors) > 0
        assert "missing 'entries' field" in result.errors[0]

    def test_import_json_malformed(self, service, temp_dir):
        """Test importing malformed JSON."""
        json_path = temp_dir / "malformed.json"
        json_path.write_text("{invalid json content")
        
        result = service.import_json_format(json_path)
        
        assert result.success is False
        assert result.total_processed == 0
        assert result.successfully_imported == 0
        assert len(result.errors) > 0
        assert "Invalid JSON file" in result.errors[0]

    def test_import_csv_missing_required_columns(self, service, temp_dir):
        """Test importing CSV with missing required columns."""
        csv_path = temp_dir / "missing_columns.csv"
        with open(csv_path, 'w', newline='') as f:
            writer = csv.writer(f)
            writer.writerow(['ip_address', 'comment'])  # Missing 'hostnames'
            writer.writerow(['127.0.0.1', 'test'])
        
        result = service.import_csv_format(csv_path)
        
        assert result.success is False
        assert result.total_processed == 0
        assert result.successfully_imported == 0
        assert len(result.errors) > 0
        assert "Missing required columns" in result.errors[0]

    def test_import_json_with_warnings(self, service, temp_dir):
        """Test importing JSON with warnings (invalid dates)."""
        json_data = {
            "entries": [
                {
                    "ip_address": "127.0.0.1",
                    "hostnames": ["localhost"],
                    "comment": "Test",
                    "is_active": True,
                    "last_resolved": "invalid-date-format"
                }
            ]
        }
        
        json_path = temp_dir / "warnings.json"
        with open(json_path, 'w') as f:
            json.dump(json_data, f)
        
        result = service.import_json_format(json_path)
        
        assert result.success is True
        assert result.total_processed == 1
        assert result.successfully_imported == 1
        assert len(result.warnings) > 0
        assert "Invalid last_resolved date format" in result.warnings[0]

    def test_import_nonexistent_file(self, service):
        """Test importing non-existent file."""
        nonexistent_path = Path("/nonexistent/file.json")
        
        result = service.import_json_format(nonexistent_path)
        
        assert result.success is False
        assert result.total_processed == 0
        assert result.successfully_imported == 0
        assert len(result.errors) > 0

    # Utility Tests

    def test_detect_file_format_by_extension(self, service, temp_dir):
        """Test file format detection by extension."""
        json_file = temp_dir / "test.json"
        csv_file = temp_dir / "test.csv"
        hosts_file = temp_dir / "hosts"
        txt_file = temp_dir / "test.txt"
        
        # Create empty files
        for f in [json_file, csv_file, hosts_file, txt_file]:
            f.touch()
        
        assert service.detect_file_format(json_file) == ImportFormat.JSON
        assert service.detect_file_format(csv_file) == ImportFormat.CSV
        assert service.detect_file_format(hosts_file) == ImportFormat.HOSTS
        assert service.detect_file_format(txt_file) == ImportFormat.HOSTS

    def test_detect_file_format_by_content(self, service, temp_dir):
        """Test file format detection by content."""
        # JSON content
        json_file = temp_dir / "no_extension"
        json_file.write_text('{"entries": []}')
        assert service.detect_file_format(json_file) == ImportFormat.JSON
        
        # CSV content
        csv_file = temp_dir / "csv_no_ext"
        csv_file.write_text('ip_address,hostnames,comment')
        assert service.detect_file_format(csv_file) == ImportFormat.CSV
        
        # Hosts content
        hosts_file = temp_dir / "hosts_no_ext"
        hosts_file.write_text('127.0.0.1 localhost')
        assert service.detect_file_format(hosts_file) == ImportFormat.HOSTS

    def test_detect_file_format_nonexistent(self, service):
        """Test file format detection for non-existent file."""
        result = service.detect_file_format(Path("/nonexistent/file.txt"))
        assert result is None

    def test_validate_export_path(self, service, temp_dir):
        """Test export path validation."""
        # Valid path
        valid_path = temp_dir / "export.json"
        warnings = service.validate_export_path(valid_path, ExportFormat.JSON)
        assert len(warnings) == 0
        
        # Existing file
        existing_file = temp_dir / "existing.json"
        existing_file.touch()
        warnings = service.validate_export_path(existing_file, ExportFormat.JSON)
        assert any("already exists" in w for w in warnings)
        
        # Wrong extension
        wrong_ext = temp_dir / "file.txt"
        warnings = service.validate_export_path(wrong_ext, ExportFormat.JSON)
        assert any("doesn't match format" in w for w in warnings)

    def test_validate_export_path_invalid_directory(self, service):
        """Test export path validation with invalid directory."""
        invalid_path = Path("/invalid/nonexistent/directory/file.json")
        warnings = service.validate_export_path(invalid_path, ExportFormat.JSON)
        assert any("does not exist" in w for w in warnings)

    # Integration Tests

    def test_export_import_roundtrip_json(self, service, sample_hosts_file, temp_dir):
        """Test export-import roundtrip for JSON format."""
        export_path = temp_dir / "roundtrip.json"
        
        # Export
        export_result = service.export_json_format(sample_hosts_file, export_path)
        assert export_result.success is True
        
        # Import
        import_result = service.import_json_format(export_path)
        assert import_result.success is True
        assert import_result.successfully_imported == len(sample_hosts_file.entries)
        
        # Verify data integrity
        original_entries = sample_hosts_file.entries
        imported_entries = import_result.entries
        
        assert len(imported_entries) == len(original_entries)
        
        # Check specific entries
        for orig, imported in zip(original_entries, imported_entries):
            assert orig.ip_address == imported.ip_address
            assert orig.hostnames == imported.hostnames
            assert orig.comment == imported.comment
            assert orig.is_active == imported.is_active
            assert orig.dns_name == imported.dns_name

    def test_export_import_roundtrip_csv(self, service, sample_hosts_file, temp_dir):
        """Test export-import roundtrip for CSV format."""
        export_path = temp_dir / "roundtrip.csv"
        
        # Export
        export_result = service.export_csv_format(sample_hosts_file, export_path)
        assert export_result.success is True
        
        # Import
        import_result = service.import_csv_format(export_path)
        assert import_result.success is True
        assert import_result.successfully_imported == len(sample_hosts_file.entries)

    def test_import_result_properties(self):
        """Test ImportResult properties."""
        # Result with errors
        result_with_errors = ImportResult(
            success=False,
            entries=[],
            errors=["Error 1", "Error 2"],
            warnings=[],
            total_processed=5,
            successfully_imported=0
        )
        assert result_with_errors.has_errors is True
        assert result_with_errors.has_warnings is False
        
        # Result with warnings
        result_with_warnings = ImportResult(
            success=True,
            entries=[],
            errors=[],
            warnings=["Warning 1"],
            total_processed=5,
            successfully_imported=5
        )
        assert result_with_warnings.has_errors is False
        assert result_with_warnings.has_warnings is True

    def test_empty_hosts_file_export(self, service, temp_dir):
        """Test exporting empty hosts file."""
        empty_hosts_file = HostsFile()
        export_path = temp_dir / "empty.json"
        
        result = service.export_json_format(empty_hosts_file, export_path)
        
        assert result.success is True
        assert result.entries_exported == 0
        assert export_path.exists()
        
        # Verify empty file structure
        with open(export_path, 'r') as f:
            data = json.load(f)
        assert data["metadata"]["total_entries"] == 0
        assert len(data["entries"]) == 0

    def test_large_hostnames_list_csv(self, service, temp_dir):
        """Test CSV export/import with large hostnames list."""
        entry = HostEntry(
            "192.168.1.1", 
            ["host1.com", "host2.com", "host3.com", "host4.com", "host5.com"],
            "Multiple hostnames",
            True
        )
        hosts_file = HostsFile()
        hosts_file.entries = [entry]
        
        export_path = temp_dir / "multi_hostnames.csv"
        
        # Export
        export_result = service.export_csv_format(hosts_file, export_path)
        assert export_result.success is True
        
        # Import
        import_result = service.import_csv_format(export_path)
        assert import_result.success is True
        
        imported_entry = import_result.entries[0]
        assert len(imported_entry.hostnames) == 5
        assert "host1.com" in imported_entry.hostnames
        assert "host5.com" in imported_entry.hostnames
