"""Tests for SOC2 compliance features."""

import json
import os
import tempfile
from pathlib import Path

import pytest

from arzule_ingest.config import ArzuleConfig
from arzule_ingest.sanitize import sanitize, redact_pii, redact_secrets
from arzule_ingest.sinks.http_batch import HttpBatchSink, TLSRequiredError
from arzule_ingest.sinks.file_jsonl import JsonlFileSink
from arzule_ingest.audit import AuditLogger


class TestTLSEnforcement:
    """Tests for TLS enforcement in HTTP sink."""

    def test_rejects_http_url_by_default(self):
        """HTTP URLs should be rejected when require_tls=True (default)."""
        with pytest.raises(TLSRequiredError) as exc_info:
            HttpBatchSink(
                endpoint_url="http://example.com/ingest",
                api_key="test-key",
            )
        assert "SOC2 compliance requires HTTPS" in str(exc_info.value)

    def test_accepts_https_url(self):
        """HTTPS URLs should be accepted."""
        # This will fail to connect but should not raise TLSRequiredError
        sink = HttpBatchSink(
            endpoint_url="https://example.com/ingest",
            api_key="test-key",
        )
        assert sink.endpoint_url == "https://example.com/ingest"
        sink.close()

    def test_allows_http_when_tls_disabled(self):
        """HTTP URLs should be allowed when require_tls=False."""
        sink = HttpBatchSink(
            endpoint_url="http://localhost:8080/ingest",
            api_key="test-key",
            require_tls=False,
        )
        assert sink.endpoint_url == "http://localhost:8080/ingest"
        sink.close()


class TestPIIRedaction:
    """Tests for PII redaction in sanitize."""

    def test_redacts_email_by_default(self):
        """Emails should be redacted by default."""
        result = sanitize({"message": "Contact john@example.com for help"})
        assert "[PII_REDACTED]" in result["message"]
        assert "john@example.com" not in result["message"]

    def test_redacts_phone_by_default(self):
        """Phone numbers should be redacted by default."""
        result = sanitize({"phone": "Call me at 555-123-4567"})
        assert "[PII_REDACTED]" in result["phone"]
        assert "555-123-4567" not in result["phone"]

    def test_redacts_ssn_by_default(self):
        """SSNs should be redacted by default."""
        result = sanitize({"ssn": "My SSN is 123-45-6789"})
        assert "[PII_REDACTED]" in result["ssn"]
        assert "123-45-6789" not in result["ssn"]

    def test_redacts_secrets_in_strings(self):
        """Secrets in string values should be redacted."""
        result = sanitize({"config": "api_key=sk-1234567890abcdefghij"})
        assert "[REDACTED]" in result["config"]

    def test_can_disable_pii_redaction(self):
        """PII redaction can be disabled."""
        result = sanitize(
            {"message": "Contact john@example.com"},
            redact_pii_flag=False,
        )
        assert "john@example.com" in result["message"]


class TestFileEncryption:
    """Tests for file encryption in JSONL sink."""

    @pytest.fixture
    def temp_dir(self, tmp_path_factory):
        """Create a temp dir in the workspace for sandbox compatibility."""
        # Use workspace-relative path for sandbox compatibility
        test_dir = Path(__file__).parent / ".test_tmp"
        test_dir.mkdir(exist_ok=True)
        yield test_dir
        # Cleanup
        import shutil
        if test_dir.exists():
            shutil.rmtree(test_dir, ignore_errors=True)

    def test_encrypts_file_when_enabled(self, temp_dir):
        """File contents should be encrypted when encrypt=True."""
        pytest.importorskip("cryptography")

        path = temp_dir / "test_encrypt.jsonl"
        sink = JsonlFileSink(path, encrypt=True)
        sink.write({"test": "data"})
        sink.close()

        # Read raw file - should be base64-encoded encrypted data
        content = path.read_text()
        lines = content.strip().split("\n")
        assert len(lines) == 1

        # Should not be valid JSON (it's encrypted)
        with pytest.raises(json.JSONDecodeError):
            json.loads(lines[0])

        # Cleanup
        path.unlink(missing_ok=True)
        Path(str(path) + ".key").unlink(missing_ok=True)

    def test_generates_key_file(self, temp_dir):
        """Key file should be generated when encrypt=True."""
        pytest.importorskip("cryptography")

        path = temp_dir / "test_keygen.jsonl"
        sink = JsonlFileSink(path, encrypt=True)
        sink.close()

        key_path = Path(str(path) + ".key")
        assert key_path.exists()

        # Key file should have restrictive permissions (0o600)
        mode = key_path.stat().st_mode & 0o777
        assert mode == 0o600

        # Cleanup
        path.unlink(missing_ok=True)
        key_path.unlink(missing_ok=True)


class TestConfigDefaults:
    """Tests for SOC2-compliant config defaults."""

    def test_pii_redaction_enabled_by_default(self):
        """PII redaction should be enabled by default."""
        config = ArzuleConfig(
            tenant_id="test",
            project_id="test",
        )
        assert config.redact_pii is True

    def test_tls_required_by_default(self):
        """TLS should be required by default."""
        config = ArzuleConfig(
            tenant_id="test",
            project_id="test",
        )
        assert config.require_tls is True

    def test_audit_logging_enabled_by_default(self):
        """Audit logging should be enabled by default."""
        config = ArzuleConfig(
            tenant_id="test",
            project_id="test",
        )
        assert config.audit_log_enabled is True


class TestAuditLogging:
    """Tests for audit logging."""

    def test_audit_log_writes_events(self):
        """Audit log should write events to file."""
        with tempfile.TemporaryDirectory() as tmpdir:
            log_path = Path(tmpdir) / "audit.log"
            logger = AuditLogger(log_path=log_path, enabled=True)

            logger.log(
                event_type="test",
                action="test_action",
                resource="/test/resource",
            )

            content = log_path.read_text()
            record = json.loads(content.strip())

            assert record["event_type"] == "test"
            assert record["action"] == "test_action"
            assert "timestamp" in record

    def test_audit_log_sanitizes_sensitive_details(self):
        """Audit log should redact sensitive details."""
        with tempfile.TemporaryDirectory() as tmpdir:
            log_path = Path(tmpdir) / "audit.log"
            logger = AuditLogger(log_path=log_path, enabled=True)

            logger.log(
                event_type="test",
                action="test",
                details={"password": "secret123", "safe": "value"},
            )

            content = log_path.read_text()
            record = json.loads(content.strip())

            assert record["details"]["password"] == "<redacted>"
            assert record["details"]["safe"] == "value"

    def test_audit_log_disabled(self):
        """Audit log should not write when disabled."""
        with tempfile.TemporaryDirectory() as tmpdir:
            log_path = Path(tmpdir) / "audit.log"
            logger = AuditLogger(log_path=log_path, enabled=False)

            logger.log(event_type="test", action="test")

            assert not log_path.exists()

