"""Author: Vasiliy Zdanovskiy
email: vasilyvz@gmail.com

Typed transfer-layer exceptions with structured fields for adapter consumers.
"""

from __future__ import annotations


class TransferError(Exception):
    def __init__(
        self,
        message: str,
        *,
        transfer_id: str | None = None,
        expected_offset: int | None = None,
        actual_offset: int | None = None,
        checksum_expected: str | None = None,
        checksum_actual: str | None = None,
        compression: str | None = None,
        phase: str | None = None,
    ) -> None:
        super().__init__(message)
        self.transfer_id = transfer_id
        self.expected_offset = expected_offset
        self.actual_offset = actual_offset
        self.checksum_expected = checksum_expected
        self.checksum_actual = checksum_actual
        self.compression = compression
        self.phase = phase

    def to_dict(self) -> dict[str, object]:
        result: dict[str, object] = {
            "exception_type": self.__class__.__name__,
            "message": str(self),
        }
        if self.transfer_id is not None:
            result["transfer_id"] = self.transfer_id
        if self.expected_offset is not None:
            result["expected_offset"] = self.expected_offset
        if self.actual_offset is not None:
            result["actual_offset"] = self.actual_offset
        if self.checksum_expected is not None:
            result["checksum_expected"] = self.checksum_expected
        if self.checksum_actual is not None:
            result["checksum_actual"] = self.checksum_actual
        if self.compression is not None:
            result["compression"] = self.compression
        if self.phase is not None:
            result["phase"] = self.phase
        return result


class TransferSessionNotFoundError(TransferError):
    def __init__(
        self,
        message: str,
        *,
        transfer_id: str | None = None,
        phase: str | None = "lookup",
    ) -> None:
        super().__init__(message, transfer_id=transfer_id, phase=phase)


class TransferResumeConflictError(TransferError):
    def __init__(
        self,
        message: str,
        *,
        transfer_id: str | None = None,
        expected_offset: int,
        actual_offset: int,
        phase: str | None = "resume",
    ) -> None:
        super().__init__(
            message,
            transfer_id=transfer_id,
            expected_offset=expected_offset,
            actual_offset=actual_offset,
            phase=phase,
        )


class TransferChecksumMismatchError(TransferError):
    def __init__(
        self,
        message: str,
        *,
        transfer_id: str | None = None,
        checksum_expected: str,
        checksum_actual: str,
        phase: str | None = "verify",
    ) -> None:
        super().__init__(
            message,
            transfer_id=transfer_id,
            checksum_expected=checksum_expected,
            checksum_actual=checksum_actual,
            phase=phase,
        )


class TransferCompressionError(TransferError):
    def __init__(
        self,
        message: str,
        *,
        transfer_id: str | None = None,
        compression: str | None = None,
        phase: str | None = "compress",
    ) -> None:
        super().__init__(
            message,
            transfer_id=transfer_id,
            compression=compression,
            phase=phase,
        )


class TransferTooLargeError(TransferError):
    def __init__(
        self,
        message: str,
        *,
        transfer_id: str | None = None,
        phase: str | None = "limit",
    ) -> None:
        super().__init__(message, transfer_id=transfer_id, phase=phase)


class TransferRangeNotSatisfiableError(TransferError):
    def __init__(
        self,
        message: str,
        *,
        transfer_id: str | None = None,
        expected_offset: int | None = None,
        actual_offset: int | None = None,
        phase: str | None = "range",
    ) -> None:
        super().__init__(
            message,
            transfer_id=transfer_id,
            expected_offset=expected_offset,
            actual_offset=actual_offset,
            phase=phase,
        )


class TransferExpiredError(TransferError):
    def __init__(
        self,
        message: str,
        *,
        transfer_id: str | None = None,
        phase: str | None = "ttl",
    ) -> None:
        super().__init__(message, transfer_id=transfer_id, phase=phase)


class TransferAckError(TransferError):
    """Client transfer_ack rejected (checksum, state, unknown transfer)."""

    def __init__(
        self,
        message: str,
        *,
        transfer_id: str | None = None,
        phase: str | None = "ack",
    ) -> None:
        super().__init__(message, transfer_id=transfer_id, phase=phase)
