from __future__ import annotations

import json
from pathlib import Path

import pytest

from checkin_cli import nutrition_onboarding as domain


def authority() -> domain.OnboardingAuthority:
    return domain.OnboardingAuthority(
        customer_key="client_001",
        customer_user_id=10,
        customer_chat_id=10,
        customer_topic_id=0,
        owner_user_id=12,
        owner_chat_id=12,
        owner_topic_id=0,
        consent_notice_version="privacy-v1",
        consent_granted=True,
        customer_enabled=False,
    )


def evidence(*, actor: int, message: int) -> domain.MessageEvidence:
    return domain.MessageEvidence(
        actor_user_id=actor,
        chat_id=actor,
        topic_id=0,
        message_id=message,
        update_id=message + 100,
    )


def complete_answers(service: domain.NutritionOnboardingService) -> None:
    auth = authority()
    service.start_or_resume(authority=auth, evidence=evidence(actor=10, message=1))
    for index, field in enumerate(domain.QUESTION_FIELDS, start=2):
        service.submit_answer(
            field=field,
            value=domain.example_answer(field),
            authority=auth,
            evidence=evidence(actor=10, message=index),
        )


def test_owner_v1_api_has_only_owner_and_customer_authority() -> None:
    assert domain.NUTRITION_ONBOARDING_API_VERSION == "2.0"
    dumped = authority().model_dump(mode="json")
    assert set(dumped) == {
        "customer_key", "customer_user_id", "customer_chat_id", "customer_topic_id",
        "owner_user_id", "owner_chat_id", "owner_topic_id", "consent_notice_version",
        "consent_granted", "customer_enabled",
    }


def test_attestation_goes_directly_to_owner_and_all_owner_decisions(tmp_path: Path) -> None:
    service = domain.NutritionOnboardingService(
        profile_root=tmp_path,
        customer_key="client_001",
        enforce_current_authority=False,
    )
    complete_answers(service)
    status = service.attest_baseline(
        authority=authority(), evidence=evidence(actor=10, message=40)
    )
    assert status.state.value == "owner_review"
    workflow = json.loads(service.session_path.read_text())
    assert workflow["schema_version"] == "2.0"
    assert workflow["review_flow"] == "owner_v1"
    assert workflow["owner_reviewed"] is False

    held = service.review_as_owner(
        decision="safety_hold",
        authority=authority(),
        evidence=evidence(actor=12, message=41),
    )
    assert held.state.value == "safety_hold"
    cleared = service.record_clinical_review(
        cleared=True,
        external_reference="opaque-clearance-1",
        authority=authority(),
        evidence=evidence(actor=12, message=42),
    )
    assert cleared.state.value == "owner_review"


def test_owner_approve_needs_no_owner_receipt(tmp_path: Path) -> None:
    service = domain.NutritionOnboardingService(
        profile_root=tmp_path,
        customer_key="client_001",
        enforce_current_authority=False,
    )
    complete_answers(service)
    service.attest_baseline(
        authority=authority(), evidence=evidence(actor=10, message=40)
    )
    status = service.review_as_owner(
        decision="approved",
        authority=authority(),
        evidence=evidence(actor=12, message=41),
    )
    assert status.state.value == "finalizing"
    workflow = json.loads(service.session_path.read_text())
    assert workflow["owner_reviewed"] is True
    assert len(workflow["owner_review_receipt"]) == 64


def test_owner_risk_acceptance_returns_hold_to_owner_review(
    tmp_path: Path,
) -> None:
    service = domain.NutritionOnboardingService(
        profile_root=tmp_path,
        customer_key="client_001",
        enforce_current_authority=False,
        enforce_reconciliation=True,
    )
    complete_answers(service)
    answers = service.reconciliation_answers(authority=authority())
    service.record_reconciliation(
        answers_digest=domain.canonical_digest(answers),
        advisory={"status": "unavailable"},
        clarifications=[],
        authority=authority(),
    )
    service.attest_baseline(
        authority=authority(),
        evidence=evidence(actor=10, message=40),
    )
    service.review_as_owner(
        decision="safety_hold",
        authority=authority(),
        evidence=evidence(actor=12, message=41),
    )

    held = service.status()
    reconciliation = service.reconciliation_record(
        authority=authority(),
    )
    assert reconciliation is not None
    binding = domain.OwnerRiskAcceptanceBinding(
        candidate_epoch="c" * 64,
        bootstrap_session_id="session-1",
        customer_key="client_001",
        baseline_digest=str(held.baseline_digest),
        reconciliation_digest=str(reconciliation["digest"]),
    )
    status = service.record_owner_risk_acceptance(
        request=domain.OwnerRiskAcceptanceRequest(
            binding=binding,
            acceptance_identity=domain.owner_risk_acceptance_identity(
                binding,
            ),
            authority=authority(),
            evidence=evidence(actor=12, message=42),
        ),
    )

    assert status.state.value == "owner_review"
    workflow = json.loads(service.session_path.read_text())
    receipt = workflow["owner_risk_acceptance_receipts"][0]
    assert receipt["clinical_approval"] is False
    assert receipt["decision"] == "accepted_for_nonmedical_coaching"
    assert receipt["candidate_epoch"] == "c" * 64
    assert receipt["customer_key"] == "client_001"
    assert receipt["baseline_digest"] == service.status().baseline_digest

    service.review_as_owner(
        decision="safety_hold",
        authority=authority(),
        evidence=evidence(actor=12, message=43),
    )
    baseline_text = service.baseline_path.read_text()
    tampered_baseline = json.loads(baseline_text)
    tampered_baseline["baseline"]["weight_kg"] = 999
    service.baseline_path.write_text(json.dumps(tampered_baseline))
    with pytest.raises(ValueError, match="baseline"):
        service.record_owner_risk_acceptance(
            request=domain.OwnerRiskAcceptanceRequest(
                binding=binding,
                acceptance_identity=(
                    domain.owner_risk_acceptance_identity(binding)
                ),
                authority=authority(),
                evidence=evidence(actor=12, message=44),
            ),
        )
    service.baseline_path.write_text(baseline_text)
    workflow_text = service.session_path.read_text()
    tampered_workflow = json.loads(workflow_text)
    tampered_workflow["reconciliation"]["state"] = "tampered"
    service.session_path.write_text(json.dumps(tampered_workflow))
    with pytest.raises(ValueError):
        service.record_owner_risk_acceptance(
            request=domain.OwnerRiskAcceptanceRequest(
                binding=binding,
                acceptance_identity=(
                    domain.owner_risk_acceptance_identity(binding)
                ),
                authority=authority(),
                evidence=evidence(actor=12, message=44),
            ),
        )
    service.session_path.write_text(workflow_text)
    service.record_owner_risk_acceptance(
        request=domain.OwnerRiskAcceptanceRequest(
            binding=binding,
            acceptance_identity=domain.owner_risk_acceptance_identity(
                binding,
            ),
            authority=authority(),
            evidence=evidence(actor=12, message=44),
        ),
    )
    repeated = json.loads(service.session_path.read_text())
    receipts = repeated["owner_risk_acceptance_receipts"]
    assert len(receipts) == 2
    assert receipts[0]["telegram_evidence"]["update_id"] == 142
    assert receipts[1]["telegram_evidence"]["update_id"] == 144
