"""发布运行观察的确定性分层抽样与加密来源绑定。"""

from __future__ import annotations

from datetime import UTC, datetime
from typing import Any

from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from app.core.secret_box import decrypt_secret, encrypt_secret
from app.models.agent_asset_release_telemetry import (
    AgentAssetReleaseAuditSample,
    AgentAssetReleaseObservation,
)
from app.services.agent_asset_release_telemetry_crypto import (
    deterministic_fingerprint,
    json_fingerprint,
    stable_uuid,
)

_PPM = 1_000_000


class AgentAssetReleaseSamplingService:
    """正例全量、分歧全量、其余负例按不可预测稳定分数抽检。"""

    def __init__(self, db: Session) -> None:
        self.db = db

    def ensure_sample(
        self,
        *,
        observation: AgentAssetReleaseObservation,
        source_reference: str,
        release_state: dict[str, Any],
    ) -> AgentAssetReleaseAuditSample | None:
        if observation.runtime_status != "completed":
            return None
        existing = self._sample_for_observation(observation)
        if existing is not None:
            return existing

        stratum, probability = _sampling_plan(observation, release_state)
        score = _selection_score(observation)
        if stratum == "candidate_negative_random" and score >= probability:
            return None
        identity = {
            "tenant_id": observation.tenant_id,
            "observation_id": observation.id,
            "asset_id": observation.asset_id,
            "release_id": observation.release_id,
            "stage": observation.stage,
            "version": observation.version,
            "stratum": stratum,
            "sampling_probability_ppm": probability,
            "selection_score_ppm": score,
        }
        payload_fingerprint = json_fingerprint(
            {**identity, "source_fingerprint": observation.source_fingerprint}
        )
        idempotency_key = (
            "aras:"
            + deterministic_fingerprint(
                observation.tenant_id,
                observation.id,
                observation.release_id,
            )[:59]
        )
        item = AgentAssetReleaseAuditSample(
            id=stable_uuid(idempotency_key),
            **identity,
            source_reference_encrypted=encrypt_secret(
                _required(source_reference, "source_reference", 500)
            ),
            idempotency_key=idempotency_key,
            payload_fingerprint=payload_fingerprint,
            created_at=datetime.now(UTC),
        )
        try:
            with self.db.begin_nested():
                self.db.add(item)
                self.db.flush()
        except IntegrityError:
            replay = self._sample_for_observation(observation)
            if replay is None or replay.payload_fingerprint != payload_fingerprint:
                raise
            return replay
        return item

    def source_reference(
        self,
        *,
        sample: AgentAssetReleaseAuditSample,
        tenant_id: str,
    ) -> str:
        if sample.tenant_id != _required(tenant_id, "tenant_id", 64):
            raise LookupError("Release audit sample not found.")
        return _required(
            decrypt_secret(sample.source_reference_encrypted),
            "source_reference",
            500,
        )

    def _sample_for_observation(
        self,
        observation: AgentAssetReleaseObservation,
    ) -> AgentAssetReleaseAuditSample | None:
        return self.db.scalar(
            select(AgentAssetReleaseAuditSample).where(
                AgentAssetReleaseAuditSample.tenant_id == observation.tenant_id,
                AgentAssetReleaseAuditSample.observation_id == observation.id,
            )
        )


def _sampling_plan(
    observation: AgentAssetReleaseObservation,
    release_state: dict[str, Any],
) -> tuple[str, int]:
    if observation.candidate_hit:
        return "candidate_positive_census", _PPM
    if observation.baseline_hit is True:
        return "candidate_disagreement_census", _PPM
    policy = release_state.get("policy")
    source = policy if isinstance(policy, dict) else {}
    percent = _bounded_int(source.get("negative_sample_percent"), default=20, low=1, high=100)
    return "candidate_negative_random", percent * 10_000


def _selection_score(observation: AgentAssetReleaseObservation) -> int:
    digest = deterministic_fingerprint(
        "release-audit-sample:v1",
        observation.tenant_id,
        observation.asset_id,
        observation.release_id,
        observation.stage,
        observation.version,
        observation.source_fingerprint,
    )
    return int(digest[:16], 16) % _PPM


def _bounded_int(value: Any, *, default: int, low: int, high: int) -> int:
    if isinstance(value, bool):
        return default
    try:
        parsed = int(value)
    except (TypeError, ValueError, OverflowError):
        return default
    return max(low, min(high, parsed))


def _required(value: Any, field: str, maximum: int) -> str:
    normalized = str(value or "").strip()
    if not normalized or len(normalized) > maximum:
        raise ValueError(f"{field} is required and must be at most {maximum} characters.")
    return normalized
