"""Security protocols and data types.

These protocols define the contracts for key storage and encryption components.
"""

from collections.abc import Iterator
from dataclasses import dataclass
from pathlib import Path
from typing import Protocol


@dataclass(frozen=True)
class EncryptedChunk:
    """An encrypted chunk of data with authentication tag."""

    nonce: bytes  # Unique nonce for this chunk
    ciphertext: bytes  # Encrypted data
    tag: bytes  # Authentication tag


class KeyStore(Protocol):
    """Protocol for OS keychain access.

    Implementations should use the OS credential store (Keychain, Credential Manager)
    to securely store the master encryption key.
    """

    def get_or_create_master_key(self) -> bytes:
        """Retrieve or generate the master encryption key.

        If the master key doesn't exist, generates a new 32-byte key
        and stores it in the OS keychain.

        Returns:
            32-byte master key.

        Raises:
            RuntimeError: If keychain is unavailable or locked.
        """
        ...

    def delete_master_key(self) -> None:
        """Delete the master key from the keychain.

        This renders all encrypted data permanently unrecoverable.

        Safe to call if key doesn't exist.
        """
        ...

    def has_master_key(self) -> bool:
        """Check if master key exists in the keychain.

        Returns:
            True if master key exists.
        """
        ...


class CryptoBox(Protocol):
    """Protocol for envelope encryption with per-meeting keys.

    Uses a master key to wrap/unwrap Data Encryption Keys (DEKs),
    which are used to encrypt actual meeting data.
    """

    def generate_dek(self) -> bytes:
        """Generate a new Data Encryption Key.

        Returns:
            32-byte random DEK.
        """
        ...

    def wrap_dek(self, dek: bytes) -> bytes:
        """Encrypt DEK with master key.

        Args:
            dek: Data Encryption Key to wrap.

        Returns:
            Encrypted DEK (can be stored in DB).
        """
        ...

    def unwrap_dek(self, wrapped_dek: bytes) -> bytes:
        """Decrypt DEK with master key.

        Args:
            wrapped_dek: Encrypted DEK from wrap_dek().

        Returns:
            Original DEK.

        Raises:
            ValueError: If decryption fails (invalid or tampered).
        """
        ...

    def encrypt_chunk(self, plaintext: bytes, dek: bytes) -> EncryptedChunk:
        """Encrypt a chunk of data with AES-GCM.

        Args:
            plaintext: Data to encrypt.
            dek: Data Encryption Key.

        Returns:
            EncryptedChunk with nonce, ciphertext, and tag.
        """
        ...

    def decrypt_chunk(self, chunk: EncryptedChunk, dek: bytes) -> bytes:
        """Decrypt a chunk of data.

        Args:
            chunk: EncryptedChunk to decrypt.
            dek: Data Encryption Key.

        Returns:
            Original plaintext.

        Raises:
            ValueError: If decryption fails (invalid or tampered).
        """
        ...


class EncryptedAssetWriter(Protocol):
    """Protocol for streaming encrypted audio writer.

    Writes audio chunks encrypted with a DEK to a file.
    """

    def open(self, path: Path, dek: bytes) -> None:
        """Open file for writing.

        Args:
            path: Path to the encrypted file.
            dek: Data Encryption Key for this file.

        Raises:
            RuntimeError: If already open.
            OSError: If file cannot be created.
        """
        ...

    def write_chunk(self, audio_bytes: bytes) -> None:
        """Write and encrypt an audio chunk.

        Args:
            audio_bytes: Raw audio data to encrypt and write.

        Raises:
            RuntimeError: If not open.
        """
        ...

    def close(self) -> None:
        """Finalize and close the file.

        Safe to call if already closed.
        """
        ...

    @property
    def is_open(self) -> bool:
        """Check if file is open for writing."""
        ...

    @property
    def bytes_written(self) -> int:
        """Total encrypted bytes written."""
        ...


class EncryptedAssetReader(Protocol):
    """Protocol for streaming encrypted audio reader.

    Reads and decrypts audio chunks from a file.
    """

    def open(self, path: Path, dek: bytes) -> None:
        """Open file for reading.

        Args:
            path: Path to the encrypted file.
            dek: Data Encryption Key for this file.

        Raises:
            RuntimeError: If already open.
            OSError: If file cannot be read.
            ValueError: If file format is invalid.
        """
        ...

    def read_chunks(self) -> Iterator[bytes]:
        """Yield decrypted audio chunks.

        Yields:
            Decrypted audio data chunks.

        Raises:
            RuntimeError: If not open.
            ValueError: If decryption fails.
        """
        ...

    def close(self) -> None:
        """Close the file.

        Safe to call if already closed.
        """
        ...

    @property
    def is_open(self) -> bool:
        """Check if file is open for reading."""
        ...
