"""
End-to-end integration tests.
"""

import pytest
from pathlib import Path
import pandas as pd
from uuid import uuid4

from app.agents.orchestrator import AgentOrchestrator, OrchestrationInput
from app.storage.metadata_store import MetadataStore
from app.storage.file_storage import FileStorage
from app.utils.schema_detection import SchemaDetector
from app.domain.models.dataset import Dataset


class TestEndToEndPipeline:
    """End-to-end pipeline execution tests."""

    @pytest.mark.asyncio
    async def test_complete_pipeline_flow(self, tmp_path, monkeypatch):
        """Test complete data transformation flow."""
        # Create test data
        df = pd.DataFrame({
            "customer_id": [1, 2, 3, 1],  # Duplicate
            "age": [25, None, 35, 25],  # Missing value
            "purchase": [100, 200, 150, 100],
        })
        
        csv_path = tmp_path / "test.csv"
        df.to_csv(csv_path, index=False)
        
        # Mock LLM response
        mock_llm_response = """```json
[
    {"operation": "drop_duplicates", "params": {}, "rationale": "Remove duplicates"},
    {"operation": "fill_missing", "params": {"column": "age", "strategy": "median"}, "rationale": "Fill ages"}
]
```"""
        
        async def mock_call_llm(self, prompt):
            return mock_llm_response
        
        from app.agents.planner import PlannerAgent
        monkeypatch.setattr(PlannerAgent, "_call_llm", mock_call_llm)
        
        # Detect schema
        detector = SchemaDetector()
        schema = detector.detect_schema(df)
        
        # Execute orchestration
        orchestrator = AgentOrchestrator()
        
        input_data = OrchestrationInput(
            natural_language="Remove duplicates and fill missing ages",
            dataset_id=uuid4(),
            dataset_path=csv_path,
            dataset_schema=schema,
            pipeline_name="Test Pipeline",
        )
        
        result = await orchestrator.execute_pipeline(input_data)
        
        # Verify result
        assert result.success
        assert result.output is not None
        assert result.output.pipeline.is_completed
        assert result.output.result_path is not None
        
        # Check result data
        result_df = pd.read_parquet(result.output.result_path)
        assert len(result_df) < len(df)  # Duplicates removed
        assert result_df["age"].isna().sum() == 0  # Missing filled

    @pytest.mark.asyncio
    async def test_pipeline_with_validation_failure(self, tmp_path, monkeypatch):
        """Test pipeline with validation that should fail."""
        # Create test data with intentional quality issues
        df = pd.DataFrame({
            "age": [25, 30, None, None, None],  # Too many nulls
            "email": ["a@test.com", "invalid", "c@test.com", "d@test.com", "e@test.com"],
        })
        
        csv_path = tmp_path / "bad_data.csv"
        df.to_csv(csv_path, index=False)
        
        # Mock LLM
        mock_llm_response = """```json
[
    {"operation": "drop_na", "params": {"subset": ["age"]}, "rationale": "Drop nulls"}
]
```"""
        
        async def mock_call_llm(self, prompt):
            return mock_llm_response
        
        from app.agents.planner import PlannerAgent
        monkeypatch.setattr(PlannerAgent, "_call_llm", mock_call_llm)
        
        detector = SchemaDetector()
        schema = detector.detect_schema(df)
        
        orchestrator = AgentOrchestrator()
        
        input_data = OrchestrationInput(
            natural_language="Remove rows with missing ages",
            dataset_id=uuid4(),
            dataset_path=csv_path,
            dataset_schema=schema,
        )
        
        result = await orchestrator.execute_pipeline(input_data)
        
        # Pipeline should complete but validation might find issues
        assert result.success
        # Note: Validation warnings are acceptable


class TestDatasetLifecycle:
    """Test complete dataset lifecycle."""

    def test_upload_profile_transform_export(self, tmp_path):
        """Test full dataset lifecycle."""
        from app.quality.profiler import DataProfiler
        from app.quality.scanner import QualityScanner
        
        # 1. Create dataset
        df = pd.DataFrame({
            "product": ["A", "B", "C"],
            "price": [10.0, 20.0, 30.0],
            "quantity": [100, 200, 150],
        })
        
        upload_path = tmp_path / "upload.csv"
        df.to_csv(upload_path, index=False)
        
        # 2. Profile dataset
        profiler = DataProfiler()
        profile = profiler.profile_dataset(df)
        
        assert len(profile.column_profiles) == 3
        
        # 3. Quality scan
        scanner = QualityScanner()
        metrics = scanner.scan(df)
        
        assert metrics.total_rows == 3
        assert metrics.duplicate_rows == 0
        
        # 4. Transform
        from app.transformations.normalization import normalize_pandas
        transformed = normalize_pandas(df, "price", method="minmax")
        
        assert transformed["price"].min() == 0.0
        assert transformed["price"].max() == 1.0
        
        # 5. Export
        export_path = tmp_path / "export.csv"
        transformed.to_csv(export_path, index=False)
        
        assert export_path.exists()
        
        # Verify exported data
        exported = pd.read_csv(export_path)
        assert len(exported) == 3


class TestMetadataPersistence:
    """Test metadata persistence across operations."""

    def test_metadata_survives_restart(self, tmp_path):
        """Test that metadata persists across store restarts."""
        db_path = tmp_path / "test.duckdb"
        
        # Create and save dataset
        store1 = MetadataStore(db_path=db_path)
        
        dataset = Dataset(
            name="Persistent Dataset",
            original_filename="persist.csv",
            file_path=Path("/tmp/persist.csv"),
            file_format="csv",
            file_size_bytes=1024,
            row_count=100,
        )
        
        store1.save_dataset(dataset)
        dataset_id = dataset.id
        
        # Close connection (simulating restart)
        del store1
        
        # Reopen and verify
        store2 = MetadataStore(db_path=db_path)
        retrieved = store2.get_dataset(dataset_id)
        
        assert retrieved is not None
        assert retrieved.name == "Persistent Dataset"
        assert retrieved.row_count == 100


class TestErrorRecovery:
    """Test error recovery and resilience."""

    @pytest.mark.asyncio
    async def test_recovery_from_transformation_error(self, tmp_path, monkeypatch):
        """Test graceful handling of transformation errors."""
        df = pd.DataFrame({
            "text_col": ["a", "b", "c"],  # Not suitable for normalization
        })
        
        csv_path = tmp_path / "error_test.csv"
        df.to_csv(csv_path, index=False)
        
        # Mock to return invalid operation
        mock_llm_response = """```json
[
    {"operation": "normalize", "params": {"column": "text_col"}, "rationale": "Try to normalize text"}
]
```"""
        
        async def mock_call_llm(self, prompt):
            return mock_llm_response
        
        from app.agents.planner import PlannerAgent
        monkeypatch.setattr(PlannerAgent, "_call_llm", mock_call_llm)
        
        detector = SchemaDetector()
        schema = detector.detect_schema(df)
        
        orchestrator = AgentOrchestrator()
        
        input_data = OrchestrationInput(
            natural_language="Normalize text column",  # Invalid request
            dataset_id=uuid4(),
            dataset_path=csv_path,
            dataset_schema=schema,
        )
        
        result = await orchestrator.execute_pipeline(input_data)
        
        # Should fail gracefully
        assert not result.success or result.output.pipeline.is_failed
