"""Exception types raised by CycloneStore and its domain modules.

These are part of the public API — callers (API endpoints) catch them
and translate to HTTP 409 Conflict responses.
"""


class AlreadyMatchedError(Exception):
    """Raised by ``CycloneStore.manual_match`` when the claim is already paired.

    The claim's ``matched_remittance_id`` is set, so any new pairing would
    clobber an existing match. Callers (the T15 API endpoint) should surface
    this as a 409 Conflict.
    """


class NotMatchedError(Exception):
    """Raised by ``CycloneStore.manual_unmatch`` when the claim has no match.

    Mirrors ``AlreadyMatchedError`` for the unpair operation. Same HTTP
    treatment: 409 Conflict at the API layer.
    """


class InvalidStateError(Exception):
    """Raised when an apply_* pure fn returns a skipped ApplyIntent.

    ``reconcile.apply_payment`` / ``apply_reversal`` may return
    ``skipped=True`` (e.g. claim already in a terminal state, or reversal
    on a non-paid claim). The store surfaces that as ``InvalidStateError``
    rather than silently pairing. The T15 API endpoint maps this to a
    409 Conflict and echoes ``current_state`` and ``activity_kind`` so
    the UI can render a precise message.
    """

    def __init__(self, current_state: str, activity_kind: str = "invalid_state"):
        self.current_state = current_state
        self.activity_kind = activity_kind
        super().__init__(
            f"invalid state {current_state} for apply (kind={activity_kind})"
        )