"""Semantic binding between Core revisions, repository refs, and deployment.

Core's active revision is generated-source authority.  For generated projects
that source normally lives on the repository preview ref.  The default branch
is publication authority and the deployment record is deployment authority;
their identifiers must not be compared as if they were interchangeable.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Mapping


IN_SYNC = "IN_SYNC"
CONTENT_IDENTICAL_DIFFERENT_IDENTIFIER = "CONTENT_IDENTICAL_DIFFERENT_IDENTIFIER"
CORE_AHEAD = "CORE_AHEAD"
REPOSITORY_AHEAD = "REPOSITORY_AHEAD"
DIVERGED = "DIVERGED"
CORE_POINTER_STALE = "CORE_POINTER_STALE"
PUBLICATION_INCOMPLETE = "PUBLICATION_INCOMPLETE"
IDENTIFIER_SEMANTICS_MISMATCH = "IDENTIFIER_SEMANTICS_MISMATCH"
INSUFFICIENT_EVIDENCE = "INSUFFICIENT_EVIDENCE"
NOT_APPLICABLE = "NOT_APPLICABLE"


@dataclass(frozen=True)
class SourceAuthorityObservation:
    generated_ref: str
    generated_sha: str | None
    publication_ref: str
    publication_sha: str | None
    deployment_sha: str | None
    reachable_refs: Mapping[str, str]
    classification: str

    @property
    def migration_source_ref(self) -> str:
        if self.classification not in {IN_SYNC, PUBLICATION_INCOMPLETE}:
            raise ValueError("source_authority_not_safe_for_migration")
        return self.generated_ref

    @property
    def migration_source_sha(self) -> str:
        if self.classification not in {IN_SYNC, PUBLICATION_INCOMPLETE}:
            raise ValueError("source_authority_not_safe_for_migration")
        if not self.generated_sha:
            raise ValueError("generated_source_sha_missing")
        return self.generated_sha


def classify_source_authority(
    *,
    generated_ref: str,
    generated_sha: str | None,
    publication_ref: str,
    publication_sha: str | None,
    deployment_sha: str | None = None,
    reachable_refs: Mapping[str, str] | None = None,
) -> SourceAuthorityObservation:
    """Classify SHA relationships without assuming all SHAs share one role."""
    refs = dict(reachable_refs or {})
    if not generated_sha or not publication_sha:
        classification = INSUFFICIENT_EVIDENCE
    elif generated_sha == publication_sha:
        classification = IN_SYNC
    elif generated_sha == refs.get(generated_ref):
        classification = PUBLICATION_INCOMPLETE
    elif generated_sha in refs.values():
        classification = IDENTIFIER_SEMANTICS_MISMATCH
    else:
        classification = CORE_POINTER_STALE
    return SourceAuthorityObservation(
        generated_ref=generated_ref,
        generated_sha=generated_sha,
        publication_ref=publication_ref,
        publication_sha=publication_sha,
        deployment_sha=deployment_sha,
        reachable_refs=refs,
        classification=classification,
    )


def require_migration_source(observation: SourceAuthorityObservation) -> tuple[str, str]:
    """Return the ref/SHA a bounded migration must actually modify."""
    return observation.migration_source_ref, observation.migration_source_sha
