"""Typed application errors used by the read-only foundation."""

from __future__ import annotations

import hashlib


class ApplicationError(Exception):
    """Base class for expected application failures."""


class InvalidConfiguration(ApplicationError):
    """Raised when configuration violates the current phase contract."""


class UnsupportedOperation(ApplicationError):
    """Raised when a capability is intentionally unavailable."""


class ContractViolation(ApplicationError):
    """Raised when a domain or port contract receives invalid data."""


_CONTRACT_REASON_CODES = {
    "active profile decision has no category": "active_decision_missing_category",
    "active shadow leads exceed max_leads": "active_lead_limit",
    "latitude_e7 is outside WGS84 bounds": "latitude_out_of_bounds",
    "longitude_e7 is outside WGS84 bounds": "longitude_out_of_bounds",
    "node coordinates are required": "node_coordinates_missing",
    "parsed object contains a duplicate tag key": "duplicate_normalized_tag_key",
    "shadow input contains a duplicate source identity": "duplicate_source_identity",
    "shadow review items exceed max_review_items": "review_item_limit",
    "tag key cannot be blank": "blank_tag_key",
    "tag key exceeds the bounded length": "tag_key_too_long",
    "tag keys and values must be strings": "non_string_tag",
    "tag value exceeds the bounded length": "tag_value_too_long",
    "version must be a positive integer": "invalid_object_version",
}


def bounded_contract_reason(error: ContractViolation) -> str:
    """Return an allowlisted reason or a one-way digest of an unknown message."""

    message = str(error)
    known = _CONTRACT_REASON_CODES.get(message)
    if known is not None:
        return known
    digest = hashlib.sha256(message.encode("utf-8", errors="replace")).hexdigest()[:12]
    return f"unknown_{digest}"


class PartialCycleError(ApplicationError):
    """Raised after durable evidence records one or more failed worker shards."""

    def __init__(self, summary: dict[str, object]) -> None:
        super().__init__("read-only cycle completed with failed shards")
        self.summary = dict(summary)


class PbfError(ApplicationError):
    """Base class for bounded PBF acquisition and parsing failures."""


class PbfValidationError(PbfError):
    """Raised when PBF evidence fails structural or checksum validation."""


class PbfDownloadRejected(PbfValidationError):
    """Raised when a download is not explicitly approved or fails validation."""


class PbfParseError(PbfError):
    """Raised when an approved PBF cannot be parsed."""


class PbfStageContractError(PbfParseError):
    """A secret-safe contract failure at a named parser boundary."""

    def __init__(self, stage: str, cause: ContractViolation) -> None:
        safe_stage = stage if stage.isidentifier() and len(stage) <= 48 else "unknown_stage"
        self.stage = safe_stage
        self.reason_code = bounded_contract_reason(cause)
        self.diagnostic_code = f"PbfStageContractError:{self.stage}:{self.reason_code}"
        super().__init__("PBF stage contract failed")


class PbfResourceLimitExceeded(PbfParseError):
    """Raised when bounded PBF parsing limits are exceeded."""


class PbfTransactionError(PbfParseError):
    """Raised when a parser failure is followed by a sink rollback failure."""
