"""Tests for GDPR privacy generator."""

from __future__ import annotations

from pathlib import Path

import pytest

from prisme.generators import GeneratorContext
from prisme.generators.backend.privacy import PrivacyGenerator
from prisme.spec.fields import FieldSpec, FieldType
from prisme.spec.model import ModelSpec
from prisme.spec.privacy import (
    DataClassification,
    FieldPrivacyConfig,
    GDPRModelConfig,
    LegalBasis,
    PrivacyConfig,
    RetentionPolicy,
)
from prisme.spec.project import ProjectSpec
from prisme.spec.stack import FileStrategy, StackSpec


@pytest.fixture
def personal_data_model() -> ModelSpec:
    """A model marked as personal data with PII fields."""
    return ModelSpec(
        name="Customer",
        timestamps=True,
        soft_delete=True,
        fields=[
            FieldSpec(
                name="name",
                type=FieldType.STRING,
                privacy=FieldPrivacyConfig(
                    classification=DataClassification.CONFIDENTIAL,
                    is_pii=True,
                    redact_in_logs=True,
                ),
            ),
            FieldSpec(
                name="email",
                type=FieldType.STRING,
                privacy=FieldPrivacyConfig(
                    classification=DataClassification.CONFIDENTIAL,
                    is_pii=True,
                    redact_in_logs=True,
                    audit_access=True,
                ),
            ),
            FieldSpec(name="status", type=FieldType.STRING),
        ],
        gdpr=GDPRModelConfig(
            is_personal_data=True,
            legal_basis=LegalBasis.CONTRACT,
            purpose="Customer relationship management",
            retention=RetentionPolicy(retention_days=730, auto_delete=True),
            anonymize_on_delete=True,
        ),
    )


@pytest.fixture
def non_personal_model() -> ModelSpec:
    """A model without personal data."""
    return ModelSpec(
        name="Product",
        fields=[
            FieldSpec(name="title", type=FieldType.STRING),
            FieldSpec(name="price", type=FieldType.FLOAT),
        ],
    )


@pytest.fixture
def privacy_stack(personal_data_model: ModelSpec, non_personal_model: ModelSpec) -> StackSpec:
    return StackSpec(
        name="test-privacy-app",
        models=[personal_data_model, non_personal_model],
    )


@pytest.fixture
def privacy_enabled_project() -> ProjectSpec:
    return ProjectSpec(
        name="test-privacy-app",
        privacy=PrivacyConfig(
            enabled=True,
            organization_name="Test Corp",
            dpo_email="dpo@test.com",
        ),
    )


@pytest.fixture
def privacy_disabled_project() -> ProjectSpec:
    return ProjectSpec(name="test-privacy-app")


@pytest.fixture
def context_enabled(
    privacy_stack: StackSpec,
    privacy_enabled_project: ProjectSpec,
    tmp_path: Path,
) -> GeneratorContext:
    return GeneratorContext(
        domain_spec=privacy_stack,
        output_dir=tmp_path,
        dry_run=True,
        project_spec=privacy_enabled_project,
    )


@pytest.fixture
def context_disabled(
    privacy_stack: StackSpec,
    privacy_disabled_project: ProjectSpec,
    tmp_path: Path,
) -> GeneratorContext:
    return GeneratorContext(
        domain_spec=privacy_stack,
        output_dir=tmp_path,
        dry_run=True,
        project_spec=privacy_disabled_project,
    )


class TestPrivacyGenerator:
    """Tests for PrivacyGenerator."""

    def test_skips_when_disabled(self, context_disabled: GeneratorContext):
        generator = PrivacyGenerator(context_disabled)
        files = generator.generate_files()
        assert files == []
        assert generator.skip_generation is True

    def test_generates_files_when_enabled(self, context_enabled: GeneratorContext):
        generator = PrivacyGenerator(context_enabled)
        files = generator.generate_files()
        # Should generate: audit_log_model, audit_service, data_export_service,
        # data_deletion_service, privacy_routes, __init__, retention_service = 7
        assert len(files) == 7

    def test_generates_audit_log_model(self, context_enabled: GeneratorContext):
        generator = PrivacyGenerator(context_enabled)
        files = generator.generate_files()

        audit_model = next(f for f in files if "audit_log_model.py" in str(f.path))
        assert "PrivacyAuditLog" in audit_model.content
        assert "privacy_audit_log" in audit_model.content
        assert "operation" in audit_model.content
        assert "model_name" in audit_model.content
        assert audit_model.strategy == FileStrategy.ALWAYS_OVERWRITE

    def test_generates_audit_service(self, context_enabled: GeneratorContext):
        generator = PrivacyGenerator(context_enabled)
        files = generator.generate_files()

        audit_svc = next(f for f in files if "audit_service.py" in str(f.path))
        assert "AuditService" in audit_svc.content
        assert "async def log(" in audit_svc.content
        assert "async def query(" in audit_svc.content

    def test_generates_data_export_service(self, context_enabled: GeneratorContext):
        generator = PrivacyGenerator(context_enabled)
        files = generator.generate_files()

        export_svc = next(f for f in files if "data_export_service.py" in str(f.path))
        assert "DataExportService" in export_svc.content
        assert "export_subject_data" in export_svc.content
        assert "Customer" in export_svc.content
        # Non-personal data model should not appear
        assert "Product" not in export_svc.content

    def test_generates_data_deletion_service(self, context_enabled: GeneratorContext):
        generator = PrivacyGenerator(context_enabled)
        files = generator.generate_files()

        deletion_svc = next(f for f in files if "data_deletion_service.py" in str(f.path))
        assert "DataDeletionService" in deletion_svc.content
        assert "erase_subject_data" in deletion_svc.content
        assert "Customer" in deletion_svc.content
        assert "_anonymize_customer" in deletion_svc.content

    def test_generates_retention_service(self, context_enabled: GeneratorContext):
        generator = PrivacyGenerator(context_enabled)
        files = generator.generate_files()

        retention_svc = next(f for f in files if "retention_service.py" in str(f.path))
        assert "RetentionService" in retention_svc.content
        assert "enforce_all" in retention_svc.content
        assert "730" in retention_svc.content  # retention_days

    def test_generates_privacy_routes(self, context_enabled: GeneratorContext):
        generator = PrivacyGenerator(context_enabled)
        files = generator.generate_files()

        routes = next(f for f in files if "routes.py" in str(f.path))
        assert "router" in routes.content
        assert "/privacy" in routes.content
        assert "export_subject_data" in routes.content
        assert "erase_subject_data" in routes.content
        assert "query_audit_log" in routes.content
        assert "retention_report" in routes.content

    def test_generates_init(self, context_enabled: GeneratorContext):
        generator = PrivacyGenerator(context_enabled)
        files = generator.generate_files()

        init_file = next(f for f in files if "__init__.py" in str(f.path))
        assert "AuditService" in init_file.content
        assert "DataExportService" in init_file.content
        assert "DataDeletionService" in init_file.content

    def test_uses_correct_package_name(self, context_enabled: GeneratorContext):
        generator = PrivacyGenerator(context_enabled)
        files = generator.generate_files()

        audit_svc = next(f for f in files if "audit_service.py" in str(f.path))
        assert "test_privacy_app" in audit_svc.content

    def test_no_retention_without_policy(self, tmp_path: Path):
        """When no model has retention, retention_service is not generated."""
        model = ModelSpec(
            name="Customer",
            fields=[FieldSpec(name="email", type=FieldType.STRING)],
            gdpr=GDPRModelConfig(is_personal_data=True),
        )
        stack = StackSpec(name="no-retention-app", models=[model])
        project = ProjectSpec(
            name="no-retention-app",
            privacy=PrivacyConfig(enabled=True, organization_name="Test"),
        )
        context = GeneratorContext(
            domain_spec=stack,
            output_dir=tmp_path,
            dry_run=True,
            project_spec=project,
        )
        generator = PrivacyGenerator(context)
        files = generator.generate_files()

        # 6 files (no retention_service)
        assert len(files) == 6
        assert not any("retention_service" in str(f.path) for f in files)

    def test_all_files_always_overwrite(self, context_enabled: GeneratorContext):
        generator = PrivacyGenerator(context_enabled)
        files = generator.generate_files()
        for f in files:
            assert f.strategy == FileStrategy.ALWAYS_OVERWRITE
