"""Tests for GDPR/NIS2 compliance documentation generator."""

from __future__ import annotations

from pathlib import Path

import pytest

from prisme.generators import GeneratorContext
from prisme.generators.backend.compliance_docs import ComplianceDocsGenerator
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 gdpr_stack() -> StackSpec:
    return StackSpec(
        name="test-compliance-app",
        models=[
            ModelSpec(
                name="Customer",
                timestamps=True,
                soft_delete=True,
                fields=[
                    FieldSpec(
                        name="name",
                        type=FieldType.STRING,
                        privacy=FieldPrivacyConfig(
                            classification=DataClassification.CONFIDENTIAL,
                            is_pii=True,
                        ),
                    ),
                    FieldSpec(
                        name="email",
                        type=FieldType.STRING,
                        privacy=FieldPrivacyConfig(
                            classification=DataClassification.CONFIDENTIAL,
                            is_pii=True,
                            redact_in_logs=True,
                            audit_access=True,
                            encrypt_at_rest=True,
                        ),
                    ),
                    FieldSpec(name="status", type=FieldType.STRING),
                ],
                gdpr=GDPRModelConfig(
                    is_personal_data=True,
                    legal_basis=LegalBasis.CONTRACT,
                    purpose="Customer management",
                    retention=RetentionPolicy(retention_days=365),
                ),
            ),
            ModelSpec(
                name="Product",
                fields=[
                    FieldSpec(name="title", type=FieldType.STRING),
                ],
            ),
        ],
    )


@pytest.fixture
def gdpr_project() -> ProjectSpec:
    return ProjectSpec(
        name="test-compliance-app",
        privacy=PrivacyConfig(
            enabled=True,
            organization_name="Test Corp",
            dpo_email="dpo@test.com",
            privacy_policy_url="https://test.com/privacy",
            data_processing_region="EU",
            subprocessors=["AWS", "Stripe"],
        ),
    )


@pytest.fixture
def nis2_project() -> ProjectSpec:
    return ProjectSpec(
        name="test-compliance-app",
        privacy=PrivacyConfig(
            enabled=True,
            organization_name="Test Corp",
            dpo_email="dpo@test.com",
            nis2_enabled=True,
            nis2_sector="digital_infrastructure",
            nis2_entity_type="essential",
        ),
    )


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


@pytest.fixture
def context_gdpr(
    gdpr_stack: StackSpec, gdpr_project: ProjectSpec, tmp_path: Path
) -> GeneratorContext:
    return GeneratorContext(
        domain_spec=gdpr_stack,
        output_dir=tmp_path,
        dry_run=True,
        project_spec=gdpr_project,
    )


@pytest.fixture
def context_nis2(
    gdpr_stack: StackSpec, nis2_project: ProjectSpec, tmp_path: Path
) -> GeneratorContext:
    return GeneratorContext(
        domain_spec=gdpr_stack,
        output_dir=tmp_path,
        dry_run=True,
        project_spec=nis2_project,
    )


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


class TestComplianceDocsGenerator:
    """Tests for ComplianceDocsGenerator."""

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

    def test_generates_gdpr_docs(self, context_gdpr: GeneratorContext):
        generator = ComplianceDocsGenerator(context_gdpr)
        files = generator.generate_files()
        # Should generate: GDPR-COMPLIANCE.md, DATA-CLASSIFICATION.md
        assert len(files) == 2

    def test_generates_nis2_when_enabled(self, context_nis2: GeneratorContext):
        generator = ComplianceDocsGenerator(context_nis2)
        files = generator.generate_files()
        # Should generate: GDPR + DATA-CLASSIFICATION + NIS2 = 3
        assert len(files) == 3
        nis2_file = next(f for f in files if "NIS2" in str(f.path))
        assert nis2_file is not None

    def test_gdpr_doc_content(self, context_gdpr: GeneratorContext):
        generator = ComplianceDocsGenerator(context_gdpr)
        files = generator.generate_files()

        gdpr_doc = next(f for f in files if "GDPR-COMPLIANCE" in str(f.path))
        assert "Test Corp" in gdpr_doc.content
        assert "dpo@test.com" in gdpr_doc.content
        assert "https://test.com/privacy" in gdpr_doc.content
        assert "Customer" in gdpr_doc.content
        assert "Contract" in gdpr_doc.content
        assert "365 days" in gdpr_doc.content
        assert "AWS" in gdpr_doc.content
        assert "Stripe" in gdpr_doc.content

    def test_gdpr_doc_includes_data_subject_rights(self, context_gdpr: GeneratorContext):
        generator = ComplianceDocsGenerator(context_gdpr)
        files = generator.generate_files()

        gdpr_doc = next(f for f in files if "GDPR-COMPLIANCE" in str(f.path))
        assert "Right to Access" in gdpr_doc.content
        assert "Right to Erasure" in gdpr_doc.content
        assert "Art. 15" in gdpr_doc.content
        assert "Art. 17" in gdpr_doc.content

    def test_classification_doc_content(self, context_gdpr: GeneratorContext):
        generator = ComplianceDocsGenerator(context_gdpr)
        files = generator.generate_files()

        class_doc = next(f for f in files if "DATA-CLASSIFICATION" in str(f.path))
        assert "Customer" in class_doc.content
        assert "Product" in class_doc.content
        assert "personal data" in class_doc.content.lower()

    def test_nis2_doc_content(self, context_nis2: GeneratorContext):
        generator = ComplianceDocsGenerator(context_nis2)
        files = generator.generate_files()

        nis2_doc = next(f for f in files if "NIS2" in str(f.path))
        assert "Test Corp" in nis2_doc.content
        assert "Essential" in nis2_doc.content
        assert "digital_infrastructure" in nis2_doc.content
        assert "Article 21" in nis2_doc.content
        assert "Article 23" in nis2_doc.content

    def test_nis2_doc_essential_penalties(self, context_nis2: GeneratorContext):
        generator = ComplianceDocsGenerator(context_nis2)
        files = generator.generate_files()

        nis2_doc = next(f for f in files if "NIS2" in str(f.path))
        assert "10,000,000" in nis2_doc.content

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

    def test_output_paths(self, context_gdpr: GeneratorContext):
        generator = ComplianceDocsGenerator(context_gdpr)
        files = generator.generate_files()

        paths = [str(f.path) for f in files]
        assert any("docs/compliance/GDPR-COMPLIANCE.md" in p for p in paths)
        assert any("docs/compliance/DATA-CLASSIFICATION.md" in p for p in paths)


class TestComplianceDocsValidatorIntegration:
    """Tests that privacy validation works with the compliance docs generator."""

    def test_validates_privacy_config(self):
        from prisme.spec.validators import validate_privacy_config

        stack = StackSpec(
            name="test",
            models=[
                ModelSpec(
                    name="User",
                    fields=[FieldSpec(name="email", type=FieldType.STRING)],
                    gdpr=GDPRModelConfig(is_personal_data=True),
                ),
            ],
        )
        project = ProjectSpec(name="test")  # privacy.enabled=False

        errors = validate_privacy_config(stack, project)
        assert len(errors) >= 1
        assert "privacy" in errors[0].lower() or "enabled" in errors[0].lower()

    def test_validates_retention_requires_timestamps(self):
        from prisme.spec.validators import validate_privacy_config

        stack = StackSpec(
            name="test",
            models=[
                ModelSpec(
                    name="User",
                    timestamps=False,
                    fields=[FieldSpec(name="email", type=FieldType.STRING)],
                    gdpr=GDPRModelConfig(
                        is_personal_data=True,
                        retention=RetentionPolicy(retention_days=365),
                    ),
                ),
            ],
        )
        project = ProjectSpec(
            name="test",
            privacy=PrivacyConfig(enabled=True, organization_name="Test"),
        )

        errors = validate_privacy_config(stack, project)
        assert any("timestamps" in e for e in errors)

    def test_validates_soft_delete_first(self):
        from prisme.spec.validators import validate_privacy_config

        stack = StackSpec(
            name="test",
            models=[
                ModelSpec(
                    name="User",
                    timestamps=True,
                    soft_delete=False,
                    fields=[FieldSpec(name="email", type=FieldType.STRING)],
                    gdpr=GDPRModelConfig(
                        is_personal_data=True,
                        retention=RetentionPolicy(
                            retention_days=365,
                            soft_delete_first=True,
                        ),
                    ),
                ),
            ],
        )
        project = ProjectSpec(
            name="test",
            privacy=PrivacyConfig(enabled=True, organization_name="Test"),
        )

        errors = validate_privacy_config(stack, project)
        assert any("soft_delete" in e for e in errors)

    def test_validates_organization_name_required(self):
        from prisme.spec.validators import validate_privacy_config

        stack = StackSpec(
            name="test",
            models=[
                ModelSpec(
                    name="Product",
                    fields=[FieldSpec(name="title", type=FieldType.STRING)],
                ),
            ],
        )
        project = ProjectSpec(
            name="test",
            privacy=PrivacyConfig(enabled=True),
        )

        errors = validate_privacy_config(stack, project)
        assert any("organization_name" in e for e in errors)

    def test_passes_with_valid_config(self):
        from prisme.spec.validators import validate_privacy_config

        stack = StackSpec(
            name="test",
            models=[
                ModelSpec(
                    name="Customer",
                    timestamps=True,
                    soft_delete=True,
                    fields=[FieldSpec(name="email", type=FieldType.STRING)],
                    gdpr=GDPRModelConfig(
                        is_personal_data=True,
                        retention=RetentionPolicy(retention_days=365),
                    ),
                ),
            ],
        )
        project = ProjectSpec(
            name="test",
            privacy=PrivacyConfig(enabled=True, organization_name="Test Corp"),
        )

        errors = validate_privacy_config(stack, project)
        assert errors == []
