"""
Unit Tests for BYOEH (Bring-Your-Own-Expectation-Horizon) Module

Tests cover:
- EH upload and storage
- Key sharing system
- Invitation flow (Invite, Accept, Decline, Revoke)
- Klausur linking
- RAG query functionality
- Audit logging
"""

import pytest
import json
import uuid
import hashlib
from datetime import datetime, timezone, timedelta
from unittest.mock import AsyncMock, MagicMock, patch
from fastapi.testclient import TestClient

# Import the main app and data structures (post-refactoring modular imports)
import sys
sys.path.insert(0, '..')
from main import app
from storage import (
    eh_db,
    eh_key_shares_db,
    eh_klausur_links_db,
    eh_audit_db,
    eh_invitations_db,
    klausuren_db,
)
from models.eh import (
    Erwartungshorizont,
    EHKeyShare,
    EHKlausurLink,
    EHShareInvitation,
)
from models.exam import Klausur
from models.enums import KlausurModus


# =============================================
# FIXTURES
# =============================================

@pytest.fixture
def client():
    """Test client for FastAPI app."""
    return TestClient(app)


@pytest.fixture
def auth_headers():
    """JWT auth headers for teacher user."""
    import jwt
    token = jwt.encode(
        {
            "user_id": "test-teacher-001",
            "email": "teacher@school.de",
            "role": "admin",
            "tenant_id": "school-001"
        },
        "your-super-secret-jwt-key-change-in-production",
        algorithm="HS256"
    )
    return {"Authorization": f"Bearer {token}"}


@pytest.fixture
def second_examiner_headers():
    """JWT auth headers for second examiner (non-admin teacher)."""
    import jwt
    token = jwt.encode(
        {
            "user_id": "test-examiner-002",
            "email": "examiner2@school.de",
            "role": "teacher",  # Non-admin to test access control
            "tenant_id": "school-001"
        },
        "your-super-secret-jwt-key-change-in-production",
        algorithm="HS256"
    )
    return {"Authorization": f"Bearer {token}"}


@pytest.fixture
def sample_eh():
    """Create a sample Erwartungshorizont."""
    eh_id = str(uuid.uuid4())
    eh = Erwartungshorizont(
        id=eh_id,
        tenant_id="school-001",
        teacher_id="test-teacher-001",
        title="Deutsch LK Abitur 2025",
        subject="deutsch",
        niveau="eA",
        year=2025,
        aufgaben_nummer="Aufgabe 1",
        encryption_key_hash="abc123" + "0" * 58,  # 64 char hash
        salt="def456" * 5 + "0" * 2,  # 32 char salt
        encrypted_file_path=f"/app/eh-uploads/school-001/{eh_id}/encrypted.bin",
        file_size_bytes=1024000,
        original_filename="erwartungshorizont.pdf",
        rights_confirmed=True,
        rights_confirmed_at=datetime.now(timezone.utc),
        status="indexed",
        chunk_count=10,
        indexed_at=datetime.now(timezone.utc),
        error_message=None,
        training_allowed=False,
        created_at=datetime.now(timezone.utc),
        deleted_at=None
    )
    eh_db[eh_id] = eh
    return eh


@pytest.fixture
def sample_klausur():
    """Create a sample Klausur."""
    klausur_id = str(uuid.uuid4())
    klausur = Klausur(
        id=klausur_id,
        title="Deutsch LK Q1",
        subject="deutsch",
        modus=KlausurModus.VORABITUR,
        class_id="class-001",
        year=2025,
        semester="Q1",
        erwartungshorizont=None,
        students=[],
        created_at=datetime.now(timezone.utc),
        teacher_id="test-teacher-001"
    )
    klausuren_db[klausur_id] = klausur
    return klausur


@pytest.fixture(autouse=True)
def cleanup():
    """Clean up databases after each test."""
    yield
    eh_db.clear()
    eh_key_shares_db.clear()
    eh_klausur_links_db.clear()
    eh_audit_db.clear()
    eh_invitations_db.clear()
    klausuren_db.clear()


@pytest.fixture
def sample_invitation(sample_eh):
    """Create a sample invitation."""
    invitation_id = str(uuid.uuid4())
    invitation = EHShareInvitation(
        id=invitation_id,
        eh_id=sample_eh.id,
        inviter_id="test-teacher-001",
        invitee_id="",
        invitee_email="examiner2@school.de",
        role="second_examiner",
        klausur_id=None,
        message="Bitte EH fuer Zweitkorrektur nutzen",
        status="pending",
        expires_at=datetime.now(timezone.utc) + timedelta(days=14),
        created_at=datetime.now(timezone.utc),
        accepted_at=None,
        declined_at=None
    )
    eh_invitations_db[invitation_id] = invitation
    return invitation


# =============================================
# EH CRUD TESTS
# =============================================

class TestEHList:
    """Tests for GET /api/v1/eh"""

    def test_list_empty(self, client, auth_headers):
        """List returns empty when no EH exist."""
        response = client.get("/api/v1/eh", headers=auth_headers)
        assert response.status_code == 200
        assert response.json() == []

    def test_list_own_eh(self, client, auth_headers, sample_eh):
        """List returns only user's own EH."""
        response = client.get("/api/v1/eh", headers=auth_headers)
        assert response.status_code == 200
        data = response.json()
        assert len(data) == 1
        assert data[0]["id"] == sample_eh.id
        assert data[0]["title"] == sample_eh.title

    def test_list_filter_by_subject(self, client, auth_headers, sample_eh):
        """List can filter by subject."""
        response = client.get("/api/v1/eh?subject=deutsch", headers=auth_headers)
        assert response.status_code == 200
        assert len(response.json()) == 1

        response = client.get("/api/v1/eh?subject=englisch", headers=auth_headers)
        assert response.status_code == 200
        assert len(response.json()) == 0

    def test_list_filter_by_year(self, client, auth_headers, sample_eh):
        """List can filter by year."""
        response = client.get("/api/v1/eh?year=2025", headers=auth_headers)
        assert response.status_code == 200
        assert len(response.json()) == 1

        response = client.get("/api/v1/eh?year=2024", headers=auth_headers)
        assert response.status_code == 200
        assert len(response.json()) == 0


class TestEHGet:
    """Tests for GET /api/v1/eh/{id}"""

    def test_get_existing_eh(self, client, auth_headers, sample_eh):
        """Get returns EH details."""
        response = client.get(f"/api/v1/eh/{sample_eh.id}", headers=auth_headers)
        assert response.status_code == 200
        data = response.json()
        assert data["id"] == sample_eh.id
        assert data["title"] == sample_eh.title
        assert data["subject"] == sample_eh.subject

    def test_get_nonexistent_eh(self, client, auth_headers):
        """Get returns 404 for non-existent EH."""
        response = client.get(f"/api/v1/eh/{uuid.uuid4()}", headers=auth_headers)
        assert response.status_code == 404


class TestEHDelete:
    """Tests for DELETE /api/v1/eh/{id}"""

    def test_delete_own_eh(self, client, auth_headers, sample_eh):
        """Owner can delete their EH."""
        response = client.delete(f"/api/v1/eh/{sample_eh.id}", headers=auth_headers)
        assert response.status_code == 200
        assert response.json()["status"] == "deleted"

        # Verify soft delete
        assert eh_db[sample_eh.id].deleted_at is not None

    def test_delete_others_eh(self, client, second_examiner_headers, sample_eh):
        """Non-owner cannot delete EH."""
        response = client.delete(f"/api/v1/eh/{sample_eh.id}", headers=second_examiner_headers)
        assert response.status_code == 403

    def test_delete_nonexistent_eh(self, client, auth_headers):
        """Delete returns 404 for non-existent EH."""
        response = client.delete(f"/api/v1/eh/{uuid.uuid4()}", headers=auth_headers)
        assert response.status_code == 404


# =============================================
# KEY SHARING TESTS
# =============================================

class TestEHSharing:
    """Tests for EH key sharing system."""

    def test_share_eh_with_examiner(self, client, auth_headers, sample_eh):
        """Owner can share EH with another examiner."""
        response = client.post(
            f"/api/v1/eh/{sample_eh.id}/share",
            headers=auth_headers,
            json={
                "user_id": "test-examiner-002",
                "role": "second_examiner",
                "encrypted_passphrase": "encrypted-secret-123",
                "passphrase_hint": "Das uebliche Passwort"
            }
        )
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "shared"
        assert data["shared_with"] == "test-examiner-002"
        assert data["role"] == "second_examiner"

    def test_share_invalid_role(self, client, auth_headers, sample_eh):
        """Sharing with invalid role fails."""
        response = client.post(
            f"/api/v1/eh/{sample_eh.id}/share",
            headers=auth_headers,
            json={
                "user_id": "test-examiner-002",
                "role": "invalid_role",
                "encrypted_passphrase": "encrypted-secret-123"
            }
        )
        assert response.status_code == 400

    def test_share_others_eh_fails(self, client, second_examiner_headers, sample_eh):
        """Non-owner cannot share EH."""
        response = client.post(
            f"/api/v1/eh/{sample_eh.id}/share",
            headers=second_examiner_headers,
            json={
                "user_id": "test-examiner-003",
                "role": "third_examiner",
                "encrypted_passphrase": "encrypted-secret-123"
            }
        )
        assert response.status_code == 403

    def test_list_shares(self, client, auth_headers, sample_eh):
        """Owner can list shares."""
        # Create a share first
        share = EHKeyShare(
            id=str(uuid.uuid4()),
            eh_id=sample_eh.id,
            user_id="test-examiner-002",
            encrypted_passphrase="encrypted",
            passphrase_hint="hint",
            granted_by="test-teacher-001",
            granted_at=datetime.now(timezone.utc),
            role="second_examiner",
            klausur_id=None,
            active=True
        )
        eh_key_shares_db[sample_eh.id] = [share]

        response = client.get(f"/api/v1/eh/{sample_eh.id}/shares", headers=auth_headers)
        assert response.status_code == 200
        data = response.json()
        assert len(data) == 1
        assert data[0]["user_id"] == "test-examiner-002"

    def test_revoke_share(self, client, auth_headers, sample_eh):
        """Owner can revoke a share."""
        share_id = str(uuid.uuid4())
        share = EHKeyShare(
            id=share_id,
            eh_id=sample_eh.id,
            user_id="test-examiner-002",
            encrypted_passphrase="encrypted",
            passphrase_hint="hint",
            granted_by="test-teacher-001",
            granted_at=datetime.now(timezone.utc),
            role="second_examiner",
            klausur_id=None,
            active=True
        )
        eh_key_shares_db[sample_eh.id] = [share]

        response = client.delete(
            f"/api/v1/eh/{sample_eh.id}/shares/{share_id}",
            headers=auth_headers
        )
        assert response.status_code == 200
        assert response.json()["status"] == "revoked"

        # Verify share is inactive
        assert not eh_key_shares_db[sample_eh.id][0].active

    def test_get_shared_with_me(self, client, second_examiner_headers, sample_eh):
        """User can see EH shared with them."""
        share = EHKeyShare(
            id=str(uuid.uuid4()),
            eh_id=sample_eh.id,
            user_id="test-examiner-002",
            encrypted_passphrase="encrypted",
            passphrase_hint="hint",
            granted_by="test-teacher-001",
            granted_at=datetime.now(timezone.utc),
            role="second_examiner",
            klausur_id=None,
            active=True
        )
        eh_key_shares_db[sample_eh.id] = [share]

        response = client.get("/api/v1/eh/shared-with-me", headers=second_examiner_headers)
        assert response.status_code == 200
        data = response.json()
        assert len(data) == 1
        assert data[0]["eh"]["id"] == sample_eh.id


# =============================================
# KLAUSUR LINKING TESTS
# =============================================

class TestEHKlausurLinking:
    """Tests for EH-Klausur linking."""

    def test_link_eh_to_klausur(self, client, auth_headers, sample_eh, sample_klausur):
        """Owner can link EH to Klausur."""
        response = client.post(
            f"/api/v1/eh/{sample_eh.id}/link-klausur",
            headers=auth_headers,
            json={"klausur_id": sample_klausur.id}
        )
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "linked"
        assert data["eh_id"] == sample_eh.id
        assert data["klausur_id"] == sample_klausur.id

    def test_get_linked_eh(self, client, auth_headers, sample_eh, sample_klausur):
        """Get linked EH for a Klausur."""
        link = EHKlausurLink(
            id=str(uuid.uuid4()),
            eh_id=sample_eh.id,
            klausur_id=sample_klausur.id,
            linked_by="test-teacher-001",
            linked_at=datetime.now(timezone.utc)
        )
        eh_klausur_links_db[sample_klausur.id] = [link]

        response = client.get(
            f"/api/v1/klausuren/{sample_klausur.id}/linked-eh",
            headers=auth_headers
        )
        assert response.status_code == 200
        data = response.json()
        assert len(data) == 1
        assert data[0]["eh"]["id"] == sample_eh.id
        assert data[0]["is_owner"] is True

    def test_unlink_eh_from_klausur(self, client, auth_headers, sample_eh, sample_klausur):
        """Owner can unlink EH from Klausur."""
        link = EHKlausurLink(
            id=str(uuid.uuid4()),
            eh_id=sample_eh.id,
            klausur_id=sample_klausur.id,
            linked_by="test-teacher-001",
            linked_at=datetime.now(timezone.utc)
        )
        eh_klausur_links_db[sample_klausur.id] = [link]

        response = client.delete(
            f"/api/v1/eh/{sample_eh.id}/link-klausur/{sample_klausur.id}",
            headers=auth_headers
        )
        assert response.status_code == 200
        assert response.json()["status"] == "unlinked"


# =============================================
# AUDIT LOG TESTS
# =============================================

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

    def test_audit_log_on_share(self, client, auth_headers, sample_eh):
        """Sharing creates audit log entry."""
        initial_count = len(eh_audit_db)

        client.post(
            f"/api/v1/eh/{sample_eh.id}/share",
            headers=auth_headers,
            json={
                "user_id": "test-examiner-002",
                "role": "second_examiner",
                "encrypted_passphrase": "encrypted-secret-123"
            }
        )

        assert len(eh_audit_db) > initial_count
        latest = eh_audit_db[-1]
        assert latest.action == "share"
        assert latest.eh_id == sample_eh.id

    def test_get_audit_log(self, client, auth_headers, sample_eh):
        """Can retrieve audit log."""
        # Create some audit entries by sharing
        client.post(
            f"/api/v1/eh/{sample_eh.id}/share",
            headers=auth_headers,
            json={
                "user_id": "test-examiner-002",
                "role": "second_examiner",
                "encrypted_passphrase": "encrypted-secret-123"
            }
        )

        response = client.get(
            f"/api/v1/eh/audit-log?eh_id={sample_eh.id}",
            headers=auth_headers
        )
        assert response.status_code == 200
        data = response.json()
        assert len(data) > 0


# =============================================
# RIGHTS TEXT TESTS
# =============================================

class TestRightsText:
    """Tests for rights confirmation text."""

    def test_get_rights_text(self, client, auth_headers):
        """Can retrieve rights confirmation text."""
        response = client.get("/api/v1/eh/rights-text", headers=auth_headers)
        assert response.status_code == 200
        data = response.json()
        assert "text" in data
        assert "version" in data
        assert "Urheberrecht" in data["text"]


# =============================================
# ENCRYPTION SERVICE TESTS
# =============================================

class TestEncryptionUtils:
    """Tests for encryption utilities (eh_pipeline.py)."""

    def test_hash_key(self):
        """Key hashing produces consistent results."""
        from korrektur.eh_pipeline import hash_key
        import os

        passphrase = "test-secret-passphrase"
        salt_hex = os.urandom(16).hex()
        hash1 = hash_key(passphrase, salt_hex)
        hash2 = hash_key(passphrase, salt_hex)

        assert hash1 == hash2
        assert len(hash1) == 64  # SHA-256 hex

    def test_verify_key_hash(self):
        """Key hash verification works correctly."""
        from korrektur.eh_pipeline import hash_key, verify_key_hash
        import os

        passphrase = "test-secret-passphrase"
        salt_hex = os.urandom(16).hex()
        key_hash = hash_key(passphrase, salt_hex)

        assert verify_key_hash(passphrase, salt_hex, key_hash) is True
        assert verify_key_hash("wrong-passphrase", salt_hex, key_hash) is False

    def test_chunk_text(self):
        """Text chunking produces correct overlap."""
        from korrektur.eh_pipeline import chunk_text

        text = "A" * 2000  # 2000 characters
        chunks = chunk_text(text, chunk_size=1000, overlap=200)

        assert len(chunks) >= 2
        # Check overlap
        assert chunks[0][-200:] == chunks[1][:200]

    def test_encrypt_decrypt_text(self):
        """Text encryption and decryption round-trip."""
        from korrektur.eh_pipeline import encrypt_text, decrypt_text

        plaintext = "Dies ist ein geheimer Text."
        passphrase = "geheim123"
        salt = "a" * 32  # 32 hex chars = 16 bytes

        encrypted = encrypt_text(plaintext, passphrase, salt)
        decrypted = decrypt_text(encrypted, passphrase, salt)

        assert decrypted == plaintext


# =============================================
# INVITATION FLOW TESTS
# =============================================

class TestEHInvitationFlow:
    """Tests for the Invite/Accept/Decline/Revoke workflow."""

    def test_invite_to_eh(self, client, auth_headers, sample_eh):
        """Owner can send invitation to share EH."""
        response = client.post(
            f"/api/v1/eh/{sample_eh.id}/invite",
            headers=auth_headers,
            json={
                "invitee_email": "zweitkorrektor@school.de",
                "role": "second_examiner",
                "message": "Bitte EH fuer Zweitkorrektur nutzen",
                "expires_in_days": 14
            }
        )
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "invited"
        assert data["invitee_email"] == "zweitkorrektor@school.de"
        assert data["role"] == "second_examiner"
        assert "invitation_id" in data
        assert "expires_at" in data

    def test_invite_invalid_role(self, client, auth_headers, sample_eh):
        """Invitation with invalid role fails."""
        response = client.post(
            f"/api/v1/eh/{sample_eh.id}/invite",
            headers=auth_headers,
            json={
                "invitee_email": "zweitkorrektor@school.de",
                "role": "invalid_role"
            }
        )
        assert response.status_code == 400

    def test_invite_by_non_owner_fails(self, client, second_examiner_headers, sample_eh):
        """Non-owner cannot send invitation."""
        response = client.post(
            f"/api/v1/eh/{sample_eh.id}/invite",
            headers=second_examiner_headers,
            json={
                "invitee_email": "drittkorrektor@school.de",
                "role": "third_examiner"
            }
        )
        assert response.status_code == 403

    def test_duplicate_pending_invitation_fails(self, client, auth_headers, sample_eh, sample_invitation):
        """Cannot send duplicate pending invitation to same user."""
        response = client.post(
            f"/api/v1/eh/{sample_eh.id}/invite",
            headers=auth_headers,
            json={
                "invitee_email": "examiner2@school.de",  # Same email as sample_invitation
                "role": "second_examiner"
            }
        )
        assert response.status_code == 409  # Conflict

    def test_list_pending_invitations(self, client, second_examiner_headers, sample_eh, sample_invitation):
        """User can see pending invitations addressed to them."""
        response = client.get("/api/v1/eh/invitations/pending", headers=second_examiner_headers)
        assert response.status_code == 200
        data = response.json()
        assert len(data) == 1
        assert data[0]["invitation"]["id"] == sample_invitation.id
        assert data[0]["eh"]["title"] == sample_eh.title

    def test_list_sent_invitations(self, client, auth_headers, sample_eh, sample_invitation):
        """Inviter can see sent invitations."""
        response = client.get("/api/v1/eh/invitations/sent", headers=auth_headers)
        assert response.status_code == 200
        data = response.json()
        assert len(data) == 1
        assert data[0]["invitation"]["id"] == sample_invitation.id

    def test_accept_invitation(self, client, second_examiner_headers, sample_eh, sample_invitation):
        """Invitee can accept invitation and get access."""
        response = client.post(
            f"/api/v1/eh/invitations/{sample_invitation.id}/accept",
            headers=second_examiner_headers,
            json={"encrypted_passphrase": "encrypted-secret-key-for-zk"}
        )
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "accepted"
        assert data["eh_id"] == sample_eh.id
        assert "share_id" in data

        # Verify invitation status updated
        assert eh_invitations_db[sample_invitation.id].status == "accepted"

        # Verify key share created
        assert sample_eh.id in eh_key_shares_db
        assert len(eh_key_shares_db[sample_eh.id]) == 1

    def test_accept_invitation_wrong_user(self, client, auth_headers, sample_eh, sample_invitation):
        """Only invitee can accept invitation."""
        # auth_headers is for teacher, not the invitee
        response = client.post(
            f"/api/v1/eh/invitations/{sample_invitation.id}/accept",
            headers=auth_headers,
            json={"encrypted_passphrase": "encrypted-secret"}
        )
        assert response.status_code == 403

    def test_accept_expired_invitation(self, client, second_examiner_headers, sample_eh):
        """Cannot accept expired invitation."""
        # Create expired invitation
        invitation_id = str(uuid.uuid4())
        expired_invitation = EHShareInvitation(
            id=invitation_id,
            eh_id=sample_eh.id,
            inviter_id="test-teacher-001",
            invitee_id="",
            invitee_email="examiner2@school.de",
            role="second_examiner",
            klausur_id=None,
            message=None,
            status="pending",
            expires_at=datetime.now(timezone.utc) - timedelta(days=1),  # Expired
            created_at=datetime.now(timezone.utc) - timedelta(days=15),
            accepted_at=None,
            declined_at=None
        )
        eh_invitations_db[invitation_id] = expired_invitation

        response = client.post(
            f"/api/v1/eh/invitations/{invitation_id}/accept",
            headers=second_examiner_headers,
            json={"encrypted_passphrase": "encrypted-secret"}
        )
        assert response.status_code == 400
        assert "expired" in response.json()["detail"].lower()

    def test_decline_invitation(self, client, second_examiner_headers, sample_invitation):
        """Invitee can decline invitation."""
        response = client.post(
            f"/api/v1/eh/invitations/{sample_invitation.id}/decline",
            headers=second_examiner_headers
        )
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "declined"

        # Verify status updated
        assert eh_invitations_db[sample_invitation.id].status == "declined"

    def test_decline_invitation_wrong_user(self, client, auth_headers, sample_invitation):
        """Only invitee can decline invitation."""
        response = client.post(
            f"/api/v1/eh/invitations/{sample_invitation.id}/decline",
            headers=auth_headers
        )
        assert response.status_code == 403

    def test_revoke_invitation(self, client, auth_headers, sample_invitation):
        """Inviter can revoke pending invitation."""
        response = client.delete(
            f"/api/v1/eh/invitations/{sample_invitation.id}",
            headers=auth_headers
        )
        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "revoked"

        # Verify status updated
        assert eh_invitations_db[sample_invitation.id].status == "revoked"

    def test_revoke_invitation_wrong_user(self, client, second_examiner_headers, sample_invitation):
        """Only inviter can revoke invitation."""
        response = client.delete(
            f"/api/v1/eh/invitations/{sample_invitation.id}",
            headers=second_examiner_headers
        )
        assert response.status_code == 403

    def test_revoke_non_pending_invitation(self, client, auth_headers, sample_eh):
        """Cannot revoke already accepted invitation."""
        invitation_id = str(uuid.uuid4())
        accepted_invitation = EHShareInvitation(
            id=invitation_id,
            eh_id=sample_eh.id,
            inviter_id="test-teacher-001",
            invitee_id="test-examiner-002",
            invitee_email="examiner2@school.de",
            role="second_examiner",
            klausur_id=None,
            message=None,
            status="accepted",
            expires_at=datetime.now(timezone.utc) + timedelta(days=14),
            created_at=datetime.now(timezone.utc),
            accepted_at=datetime.now(timezone.utc),
            declined_at=None
        )
        eh_invitations_db[invitation_id] = accepted_invitation

        response = client.delete(
            f"/api/v1/eh/invitations/{invitation_id}",
            headers=auth_headers
        )
        assert response.status_code == 400

    def test_get_access_chain(self, client, auth_headers, sample_eh, sample_invitation):
        """Owner can see complete access chain."""
        # Add a key share
        share = EHKeyShare(
            id=str(uuid.uuid4()),
            eh_id=sample_eh.id,
            user_id="test-examiner-003",
            encrypted_passphrase="encrypted",
            passphrase_hint="",
            granted_by="test-teacher-001",
            granted_at=datetime.now(timezone.utc),
            role="third_examiner",
            klausur_id=None,
            active=True
        )
        eh_key_shares_db[sample_eh.id] = [share]

        response = client.get(f"/api/v1/eh/{sample_eh.id}/access-chain", headers=auth_headers)
        assert response.status_code == 200
        data = response.json()

        assert data["eh_id"] == sample_eh.id
        assert data["owner"]["user_id"] == "test-teacher-001"
        assert len(data["active_shares"]) == 1
        assert len(data["pending_invitations"]) == 1  # sample_invitation


class TestInvitationWorkflow:
    """Integration tests for complete invitation workflow."""

    def test_complete_invite_accept_workflow(
        self, client, auth_headers, second_examiner_headers, sample_eh, sample_klausur
    ):
        """Test complete workflow: invite -> accept -> access."""
        # 1. Owner invites ZK
        response = client.post(
            f"/api/v1/eh/{sample_eh.id}/invite",
            headers=auth_headers,
            json={
                "invitee_email": "examiner2@school.de",
                "role": "second_examiner",
                "klausur_id": sample_klausur.id,
                "message": "Bitte fuer Zweitkorrektur nutzen"
            }
        )
        assert response.status_code == 200
        invitation_id = response.json()["invitation_id"]

        # 2. ZK sees pending invitation
        response = client.get("/api/v1/eh/invitations/pending", headers=second_examiner_headers)
        assert response.status_code == 200
        pending = response.json()
        assert len(pending) == 1
        assert pending[0]["invitation"]["id"] == invitation_id

        # 3. ZK accepts invitation
        response = client.post(
            f"/api/v1/eh/invitations/{invitation_id}/accept",
            headers=second_examiner_headers,
            json={"encrypted_passphrase": "encrypted-key-for-zk"}
        )
        assert response.status_code == 200

        # 4. ZK can now see EH in shared list
        response = client.get("/api/v1/eh/shared-with-me", headers=second_examiner_headers)
        assert response.status_code == 200
        shared = response.json()
        assert len(shared) == 1
        assert shared[0]["eh"]["id"] == sample_eh.id

        # 5. EK sees invitation as accepted
        response = client.get("/api/v1/eh/invitations/sent", headers=auth_headers)
        assert response.status_code == 200
        sent = response.json()
        assert len(sent) == 1
        assert sent[0]["invitation"]["status"] == "accepted"

    def test_invite_decline_reinvite_workflow(
        self, client, auth_headers, second_examiner_headers, sample_eh
    ):
        """Test workflow: invite -> decline -> re-invite."""
        # 1. Owner invites ZK
        response = client.post(
            f"/api/v1/eh/{sample_eh.id}/invite",
            headers=auth_headers,
            json={
                "invitee_email": "examiner2@school.de",
                "role": "second_examiner"
            }
        )
        assert response.status_code == 200
        invitation_id = response.json()["invitation_id"]

        # 2. ZK declines
        response = client.post(
            f"/api/v1/eh/invitations/{invitation_id}/decline",
            headers=second_examiner_headers
        )
        assert response.status_code == 200

        # 3. Owner can send new invitation (declined invitation doesn't block)
        response = client.post(
            f"/api/v1/eh/{sample_eh.id}/invite",
            headers=auth_headers,
            json={
                "invitee_email": "examiner2@school.de",
                "role": "second_examiner",
                "message": "Zweiter Versuch"
            }
        )
        assert response.status_code == 200  # New invitation allowed


# =============================================
# INTEGRATION TESTS
# =============================================

class TestEHWorkflow:
    """Integration tests for complete EH workflow."""

    def test_complete_sharing_workflow(
        self, client, auth_headers, second_examiner_headers, sample_eh, sample_klausur
    ):
        """Test complete workflow: upload -> link -> share -> access."""
        # 1. Link EH to Klausur
        response = client.post(
            f"/api/v1/eh/{sample_eh.id}/link-klausur",
            headers=auth_headers,
            json={"klausur_id": sample_klausur.id}
        )
        assert response.status_code == 200

        # 2. Share with second examiner
        response = client.post(
            f"/api/v1/eh/{sample_eh.id}/share",
            headers=auth_headers,
            json={
                "user_id": "test-examiner-002",
                "role": "second_examiner",
                "encrypted_passphrase": "encrypted-secret",
                "klausur_id": sample_klausur.id
            }
        )
        assert response.status_code == 200

        # 3. Second examiner can see shared EH
        response = client.get("/api/v1/eh/shared-with-me", headers=second_examiner_headers)
        assert response.status_code == 200
        shared = response.json()
        assert len(shared) == 1
        assert shared[0]["eh"]["id"] == sample_eh.id

        # 4. Second examiner can see linked EH for Klausur
        response = client.get(
            f"/api/v1/klausuren/{sample_klausur.id}/linked-eh",
            headers=second_examiner_headers
        )
        assert response.status_code == 200
        linked = response.json()
        assert len(linked) == 1
        assert linked[0]["is_owner"] is False
        assert linked[0]["share"] is not None


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
