"""R2 privacy-boundary tests for actual Topic-59 sidecar-shaped inputs."""

from __future__ import annotations

import hashlib
import re
import sys
from datetime import date, datetime
from pathlib import Path
from zoneinfo import ZoneInfo

import pytest


PROFILE_PACKAGE = Path(__file__).resolve().parents[2] / "dualcoach" / "profile"
if str(PROFILE_PACKAGE) not in sys.path:
    sys.path.insert(0, str(PROFILE_PACKAGE))

from checkin_cli.weekly_operations import CustomerIdentityDigest, DayState, WeeklyOperationRow

from gateway.platforms.nutrition_weekly_operations import (
    Topic59DayCardProjector,
    Topic59PublicationDisposition,
    Topic59PublicationRequest,
)
from tests.gateway._weekly_operations_authority_support import (
    authority_receipt_fixture,
    test_registry_identity as registry_identity,
)
from gateway.platforms.nutrition_weekly_operations_authority import (
    WeeklyOperationsAuthorityReceipt,
    WeeklyOperationsOwner,
    WeeklyOperationsRuntimeContext,
)
from gateway.platforms.nutrition_weekly_operations_config import WeeklyOperationsConfig, WeeklyOperationsReviewRoute
from gateway.platforms.nutrition_weekly_operations_identifiers import (
    opaque_event_id,
    opaque_status_id,
    topic59_payload_is_safe,
)
from gateway.platforms.nutrition_weekly_operations_models import (
    Topic59ApprovalState,
    Topic59DayCard,
    Topic59DayCardError,
    Topic59DeliveryState,
    Topic59DraftState,
)
from gateway.platforms.nutrition_weekly_operations_publication_contract import Topic59ProviderDelivered


_HEX = re.compile(r"^[0-9a-f]{64}$")
_KST = ZoneInfo("Asia/Seoul")
_KEY = "client_001"
_CUSTOMER = hashlib.sha256(f"nutricoach-weekly-operations-customer-identity-v1\0{_KEY}".encode()).hexdigest()
_CANDIDATE = hashlib.sha256(b"candidate").hexdigest()
_CONSENT = hashlib.sha256(b"consent").hexdigest()
_SENTINELS = (
    "name-HongGilDong", "address-Seoul010", "telegram-99887766", "weight-70.3kg",
    "calories-2300kcal", "macros-p120-c200-f60", "meal-chicken-rice", "symptom-nausea",
    "digestion-bloating", "free-text-private-note", "health-json-blood-secret",
)


@pytest.fixture
def anyio_backend() -> str:
    return "asyncio"


class _Transport:
    def __init__(self) -> None:
        self.params: list[dict[str, str]] = []
        self.edits: int = 0

    async def send(self, *, chat_id: str, topic_id: str, text: str) -> Topic59ProviderDelivered:
        self.params.append({"chat_id": chat_id, "topic_id": topic_id, "text": text})
        return Topic59ProviderDelivered("101")

    async def edit(self, *, chat_id: str, topic_id: str, message_id: str, text: str) -> Topic59ProviderDelivered:
        self.params.append({"chat_id": chat_id, "topic_id": topic_id, "message_id": message_id, "text": text})
        self.edits += 1
        return Topic59ProviderDelivered(message_id)


def _digest(value: str) -> str:
    return hashlib.sha256(value.encode("utf-8")).hexdigest()


def _request(card: Topic59DayCard) -> Topic59PublicationRequest:
    config = WeeklyOperationsConfig(
        True,
        review_route=WeeklyOperationsReviewRoute("90001", "10001"),
        registry_identity_binding_digest=registry_identity().binding_digest,
    )
    receipt = authority_receipt_fixture(
        _CANDIDATE, config.digest, (_KEY,), ("90001", "10001", 1),
        _CONSENT, datetime(2026, 8, 1, tzinfo=_KST), datetime(2026, 9, 1, tzinfo=_KST), config.feature_epoch,
    )
    runtime = WeeklyOperationsRuntimeContext(
        _CANDIDATE, _KEY, "90001", "10001", 1, _CONSENT, True, config.feature_epoch,
        datetime(2026, 8, 24, tzinfo=_KST),
    )
    return Topic59PublicationRequest(card, config, receipt, runtime)


def _actual_sidecar_row(source: str) -> WeeklyOperationRow:
    return WeeklyOperationRow(
        schema_version="nutricoach-weekly-operations-v1",
        customer_identity_digest=CustomerIdentityDigest(_CUSTOMER),
        kst_day=date(2026, 8, 24),
        state=DayState.SUBMITTED,
        canonical_sequence=7,
        canonical_digest=_digest("canonical"),
        source_event_id=source,
        source_event_digest=_digest(source),
        predecessor_row_digest="0" * 64,
        occurred_at_kst=datetime(2026, 8, 24, 23, tzinfo=_KST),
        logical_key=_digest(f"logical-{source}"),
        row_digest=_digest(f"row-{source}"),
    )


def _card(source: str) -> Topic59DayCard:
    return Topic59DayCard.from_sidecar(
        _actual_sidecar_row(source), completed_field_count=5, draft_state=Topic59DraftState.DRAFTED,
        approval_state=Topic59ApprovalState.PENDING, delivery_state=Topic59DeliveryState.NOT_SENT,
        candidate_digest=_CANDIDATE,
    )


@pytest.mark.anyio
async def test_all_raw_sidecar_sentinels_are_absent_from_every_topic59_surface(tmp_path: Path) -> None:
    cards = tuple(_card(value) for value in _SENTINELS)
    transport = _Transport()
    ledger_path = tmp_path / "topic59.jsonl"
    projector = Topic59DayCardProjector(ledger_path)

    results = [await projector.publish(_request(card), transport) for card in cards]

    surfaces = (ledger_path.read_text(encoding="utf-8"), *(str(card) for card in cards), *(str(card.payload()) for card in cards), *(str(params) for params in transport.params), *(card.render() for card in cards), *(path.name for path in tmp_path.rglob("*")))
    assert results[0].disposition is Topic59PublicationDisposition.SENT
    assert all(result.disposition is Topic59PublicationDisposition.EDITED for result in results[1:])
    assert (len(transport.params), transport.edits) == (11, 10)
    assert all(raw not in surface for raw in _SENTINELS for surface in surfaces)
    assert all(_HEX.fullmatch(card.event_id) and _HEX.fullmatch(card.status_row_id) for card in cards)


def test_opaque_ids_are_deterministic_nonreversible_and_value_validation_is_exhaustive() -> None:
    raw = _SENTINELS[-1]
    card = _card(raw)
    payload = card.payload()
    invalid = dict(payload)
    invalid["event_id"] = raw

    assert opaque_event_id(raw) == opaque_event_id(raw)
    assert opaque_event_id(raw) != opaque_status_id(raw)
    assert _HEX.fullmatch(opaque_status_id(raw)) is not None
    assert raw not in str(opaque_event_id(raw))
    assert topic59_payload_is_safe(payload)
    assert not topic59_payload_is_safe(invalid)
    with pytest.raises(Topic59DayCardError):
        _ = Topic59DayCard.from_sidecar(
            _actual_sidecar_row(raw),
            completed_field_count=65, draft_state=Topic59DraftState.DRAFTED,
            approval_state=Topic59ApprovalState.PENDING, delivery_state=Topic59DeliveryState.NOT_SENT,
            candidate_digest=_CANDIDATE,
        )
