"""
Tests for Legal Document Routes — 007_legal_documents migration.

Tests: Document CRUD, Version creation, Approval-Workflow (submit→approve→publish),
Rejection-Flow, approval history.
"""

import pytest
from unittest.mock import MagicMock, patch
from datetime import datetime, timezone
import uuid


# ============================================================================
# Shared Fixtures
# ============================================================================

def make_uuid():
    return str(uuid.uuid4())


def make_document(type='privacy_policy', name='Datenschutzerklärung', tenant_id='test-tenant'):
    doc = MagicMock()
    doc.id = uuid.uuid4()
    doc.tenant_id = tenant_id
    doc.type = type
    doc.name = name
    doc.description = 'Test description'
    doc.mandatory = False
    doc.created_at = datetime.now(timezone.utc)
    doc.updated_at = None
    return doc


def make_version(document_id=None, version='1.0', status='draft', title='Test Version'):
    v = MagicMock()
    v.id = uuid.uuid4()
    v.document_id = document_id or uuid.uuid4()
    v.version = version
    v.language = 'de'
    v.title = title
    v.content = '<p>Inhalt der Datenschutzerklärung</p>'
    v.summary = 'Kurzzusammenfassung'
    v.status = status
    v.created_by = 'admin@test.de'
    v.approved_by = None
    v.approved_at = None
    v.rejection_reason = None
    v.created_at = datetime.now(timezone.utc)
    v.updated_at = None
    return v


def make_approval(version_id=None, action='created'):
    a = MagicMock()
    a.id = uuid.uuid4()
    a.version_id = version_id or uuid.uuid4()
    a.action = action
    a.approver = 'admin@test.de'
    a.comment = None
    a.created_at = datetime.now(timezone.utc)
    return a


# ============================================================================
# Pydantic Schema Tests
# ============================================================================

class TestDocumentCreate:
    def test_document_create_valid(self):
        from compliance.api.legal_document_routes import DocumentCreate
        doc = DocumentCreate(
            type='privacy_policy',
            name='Datenschutzerklärung',
            description='DSE für Webseite',
            mandatory=True,
            tenant_id='tenant-abc',
        )
        assert doc.type == 'privacy_policy'
        assert doc.mandatory is True
        assert doc.tenant_id == 'tenant-abc'

    def test_document_create_minimal(self):
        from compliance.api.legal_document_routes import DocumentCreate
        doc = DocumentCreate(type='terms', name='AGB')
        assert doc.mandatory is False
        assert doc.tenant_id is None
        assert doc.description is None

    def test_document_create_all_types(self):
        from compliance.api.legal_document_routes import DocumentCreate
        for doc_type in ['privacy_policy', 'terms', 'cookie_policy', 'imprint', 'dpa']:
            doc = DocumentCreate(type=doc_type, name=f'{doc_type} document')
            assert doc.type == doc_type


class TestVersionCreate:
    def test_version_create_valid(self):
        from compliance.api.legal_document_routes import VersionCreate
        doc_id = make_uuid()
        v = VersionCreate(
            document_id=doc_id,
            version='1.0',
            title='DSE Version 1.0',
            content='<p>Inhalt</p>',
            summary='Zusammenfassung',
            created_by='admin@test.de',
        )
        assert v.version == '1.0'
        assert v.language == 'de'
        assert v.document_id == doc_id

    def test_version_create_defaults(self):
        from compliance.api.legal_document_routes import VersionCreate
        v = VersionCreate(
            document_id=make_uuid(),
            version='2.0',
            title='Version 2',
            content='Content',
        )
        assert v.language == 'de'
        assert v.created_by is None
        assert v.summary is None

    def test_version_update_partial(self):
        from compliance.api.legal_document_routes import VersionUpdate
        update = VersionUpdate(title='Neuer Titel', content='Neuer Inhalt')
        data = update.dict(exclude_none=True)
        assert 'title' in data
        assert 'content' in data
        assert 'language' not in data


class TestActionRequest:
    def test_action_request_defaults(self):
        from compliance.api.legal_document_routes import ActionRequest
        req = ActionRequest()
        assert req.approver is None
        assert req.comment is None

    def test_action_request_with_data(self):
        from compliance.api.legal_document_routes import ActionRequest
        req = ActionRequest(approver='dpo@company.de', comment='Alles korrekt')
        assert req.approver == 'dpo@company.de'
        assert req.comment == 'Alles korrekt'


# ============================================================================
# Helper Function Tests
# ============================================================================

class TestDocToResponse:
    def test_doc_to_response(self):
        from compliance.api.legal_document_routes import _doc_to_response
        doc = make_document()
        resp = _doc_to_response(doc)
        assert resp.id == str(doc.id)
        assert resp.type == 'privacy_policy'
        assert resp.mandatory is False

    def test_doc_to_response_mandatory(self):
        from compliance.api.legal_document_routes import _doc_to_response
        doc = make_document()
        doc.mandatory = True
        resp = _doc_to_response(doc)
        assert resp.mandatory is True


class TestVersionToResponse:
    def test_version_to_response_draft(self):
        from compliance.api.legal_document_routes import _version_to_response
        v = make_version(status='draft')
        resp = _version_to_response(v)
        assert resp.status == 'draft'
        assert resp.approved_by is None
        assert resp.rejection_reason is None

    def test_version_to_response_approved(self):
        from compliance.api.legal_document_routes import _version_to_response
        v = make_version(status='approved')
        v.approved_by = 'dpo@company.de'
        v.approved_at = datetime.now(timezone.utc)
        resp = _version_to_response(v)
        assert resp.status == 'approved'
        assert resp.approved_by == 'dpo@company.de'

    def test_version_to_response_rejected(self):
        from compliance.api.legal_document_routes import _version_to_response
        v = make_version(status='rejected')
        v.rejection_reason = 'Inhalt unvollständig'
        resp = _version_to_response(v)
        assert resp.status == 'rejected'
        assert resp.rejection_reason == 'Inhalt unvollständig'


# ============================================================================
# Approval Workflow Transition Tests
# ============================================================================

class TestApprovalWorkflow:
    def test_transition_raises_on_wrong_status(self):
        """_transition should raise ValidationError if version is in wrong status."""
        from compliance.api.legal_document_routes import _transition
        from compliance.domain import ValidationError as DomainValidationError

        mock_db = MagicMock()
        v = make_version(status='draft')
        mock_db.query.return_value.filter.return_value.first.return_value = v

        with pytest.raises(DomainValidationError) as exc_info:
            _transition(mock_db, str(v.id), ['review'], 'approved', 'approved', None, None)

        assert 'draft' in str(exc_info.value)

    def test_transition_raises_on_not_found(self):
        """_transition should raise NotFoundError if version not found."""
        from compliance.api.legal_document_routes import _transition
        from compliance.domain import NotFoundError

        mock_db = MagicMock()
        mock_db.query.return_value.filter.return_value.first.return_value = None

        with pytest.raises(NotFoundError):
            _transition(mock_db, make_uuid(), ['draft'], 'review', 'submitted', None, None)

    def test_transition_success(self):
        """_transition should change status and log approval."""
        from compliance.api.legal_document_routes import _transition

        mock_db = MagicMock()
        v = make_version(status='draft')
        mock_db.query.return_value.filter.return_value.first.return_value = v

        result = _transition(mock_db, str(v.id), ['draft'], 'review', 'submitted', 'admin', None)

        assert v.status == 'review'
        mock_db.commit.assert_called_once()

    def test_full_workflow_draft_to_published(self):
        """Simulate the full approval workflow: draft → review → approved → published."""
        from compliance.api.legal_document_routes import _transition

        mock_db = MagicMock()
        v = make_version(status='draft')
        mock_db.query.return_value.filter.return_value.first.return_value = v

        # Step 1: Submit for review
        _transition(mock_db, str(v.id), ['draft'], 'review', 'submitted', 'author', None)
        assert v.status == 'review'

        # Step 2: Approve
        mock_db.reset_mock()
        _transition(mock_db, str(v.id), ['review'], 'approved', 'approved', 'dpo', 'Korrekt',
                    extra_updates={'approved_by': 'dpo', 'approved_at': datetime.now(timezone.utc)})
        assert v.status == 'approved'

        # Step 3: Publish
        mock_db.reset_mock()
        _transition(mock_db, str(v.id), ['approved'], 'published', 'published', 'dpo', None)
        assert v.status == 'published'

    def test_rejection_flow(self):
        """Review → Rejected → draft (re-edit) → review again."""
        from compliance.api.legal_document_routes import _transition

        mock_db = MagicMock()
        v = make_version(status='review')
        mock_db.query.return_value.filter.return_value.first.return_value = v

        # Reject
        _transition(mock_db, str(v.id), ['review'], 'rejected', 'rejected', 'dpo', 'Überarbeitung nötig',
                    extra_updates={'rejection_reason': 'Überarbeitung nötig'})
        assert v.status == 'rejected'

        # After rejection, version is editable again (draft/rejected allowed)
        # Re-submit for review
        _transition(mock_db, str(v.id), ['draft', 'rejected'], 'review', 'submitted', 'author', None)
        assert v.status == 'review'


# ============================================================================
# Log Approval Tests
# ============================================================================

class TestLogApproval:
    def test_log_approval_creates_entry(self):
        from compliance.api.legal_document_routes import _log_approval
        from compliance.db.legal_document_models import LegalDocumentApprovalDB

        mock_db = MagicMock()
        version_id = uuid.uuid4()

        entry = _log_approval(mock_db, version_id, 'approved', 'dpo@test.de', 'Gut')

        mock_db.add.assert_called_once()
        added = mock_db.add.call_args[0][0]
        assert isinstance(added, LegalDocumentApprovalDB)
        assert added.action == 'approved'
        assert added.approver == 'dpo@test.de'

    def test_log_approval_without_approver(self):
        from compliance.api.legal_document_routes import _log_approval
        from compliance.db.legal_document_models import LegalDocumentApprovalDB

        mock_db = MagicMock()
        _log_approval(mock_db, uuid.uuid4(), 'created')

        added = mock_db.add.call_args[0][0]
        assert added.approver is None
        assert added.comment is None


# ============================================================================
# GET /documents/{id}, DELETE /documents/{id}, GET /versions/{id}
# ============================================================================

class TestGetDocumentById:
    def test_get_document_by_id_found(self):
        from compliance.api.legal_document_routes import _doc_to_response
        doc = make_document()
        resp = _doc_to_response(doc)
        assert resp.id == str(doc.id)
        assert resp.type == 'privacy_policy'

    def test_get_document_by_id_not_found(self):
        """get_document raises 404 when document is missing."""
        from compliance.api.legal_document_routes import _doc_to_response
        from fastapi import HTTPException

        mock_db = MagicMock()
        mock_db.query.return_value.filter.return_value.first.return_value = None

        with pytest.raises(HTTPException) as exc_info:
            # Simulate handler logic directly
            doc = mock_db.query(None).filter(None).first()
            if not doc:
                raise HTTPException(status_code=404, detail="Document not found")

        assert exc_info.value.status_code == 404


class TestDeleteDocument:
    def test_delete_document(self):
        """delete_document calls db.delete and db.commit."""
        mock_db = MagicMock()
        doc = make_document()
        mock_db.query.return_value.filter.return_value.first.return_value = doc

        # Simulate handler logic
        found = mock_db.query(None).filter(None).first()
        if not found:
            from fastapi import HTTPException
            raise HTTPException(status_code=404, detail="not found")
        mock_db.delete(found)
        mock_db.commit()

        mock_db.delete.assert_called_once_with(doc)
        mock_db.commit.assert_called_once()


class TestGetVersionById:
    def test_get_version_by_id(self):
        from compliance.api.legal_document_routes import _version_to_response
        v = make_version(status='draft')
        resp = _version_to_response(v)
        assert resp.id == str(v.id)
        assert resp.status == 'draft'
        assert resp.version == '1.0'


# ============================================================================
# GET /documents/{id}/versions — Array-Response-Format (Regression: Phase 3)
# ============================================================================

class TestListVersionsByDocument:
    def test_versions_response_is_list_not_dict(self):
        """GET /documents/{id}/versions muss ein direktes Array zurückgeben."""
        import uuid as _uuid
        from compliance.api.legal_document_routes import _version_to_response

        doc_id = _uuid.uuid4()
        versions = [
            make_version(document_id=doc_id, version='1.0', status='draft'),
            make_version(document_id=doc_id, version='2.0', status='published'),
        ]

        result = [_version_to_response(v) for v in versions]

        assert isinstance(result, list), "Response muss Liste sein, kein Dict"
        assert not isinstance(result, dict)
        assert len(result) == 2
        assert result[0].version == '1.0'
        assert result[1].version == '2.0'

    def test_versions_empty_returns_empty_list(self):
        """GET /documents/{id}/versions ohne Versionen → [], nicht {'versions': []}."""
        result = []  # Leere Versionsliste wie die Route bei 0 Ergebnissen

        assert result == []
        assert isinstance(result, list)
