class AppBlockError(Exception):
    pass


class UnknownAppBlockError(AppBlockError):
    def __init__(self, block_id: str):
        super().__init__(f"Unknown App Block: {block_id}")
        self.block_id = block_id


class InvalidAppBlockManifestError(AppBlockError):
    pass


class UnsatisfiableAppBlockPlanError(AppBlockError):
    """Execution rejected; original blocking diagnostics survive worker transport."""

    def __init__(self, blocking_gaps):
        self.blocking_gaps = tuple(gap.model_copy(deep=True) for gap in blocking_gaps)
        # Keep reconstructible args for pickle/arq, rather than only a message.
        super().__init__(self.blocking_gaps)

    def __str__(self):
        details = "; ".join(
            f"{gap.type}: {gap.id}: {gap.message}" for gap in self.blocking_gaps
        )
        return f"App Block plan cannot execute: {details}"
