from __future__ import annotations

import pytest

from k1link.device_plugins.xgrids_k1.protocol.application_bootstrap import (
    ApplicationControlAuthority,
    LiveDeviceControlBinding,
    build_shadow_bootstrap,
)
from k1link.device_plugins.xgrids_k1.protocol.application_publish import (
    OneShotPublishEnvelope,
    VendorWritesDisabledError,
    WriteDisabledOneShotPublisher,
)

PRIVATE_AUTHORITY = "11111111-2222-3333-4444-555555555555"


class SpySink:
    def __init__(self) -> None:
        self.calls = 0

    def publish(
        self,
        *,
        topic: str,
        payload: bytes,
        qos: int,
        retain: bool,
    ) -> None:
        del topic, payload, qos, retain
        self.calls += 1


def _envelope() -> OneShotPublishEnvelope:
    authority = ApplicationControlAuthority(openapi_key=PRIVATE_AUTHORITY)
    binding = LiveDeviceControlBinding(
        vendor_device_id="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
        device_serial="K1SERIAL01",
        software_version="V3.0.2-build.1",
        system_version="V3.0.2",
        device_model="LixelKity K1",
        device_type="A4",
        is_activated=True,
    )
    request = build_shadow_bootstrap(
        authority,
        binding,
        epoch_seconds=1_752_680_000,
        timezone_name="Europe/Moscow",
    ).requests[0]
    return OneShotPublishEnvelope.from_bootstrap_request(request)


def test_write_disabled_publisher_never_touches_injected_transport() -> None:
    sink = SpySink()
    publisher = WriteDisabledOneShotPublisher(sink)
    envelope = _envelope()

    for _ in range(2):
        with pytest.raises(VendorWritesDisabledError, match="cannot publish"):
            publisher.publish_once(envelope)

    assert sink.calls == 0
    assert publisher.snapshot().as_dict() == {
        "mode": "write-disabled",
        "denied_attempts": 2,
        "transport_calls": 0,
        "vendor_writes_enabled": False,
        "publisher_armed": False,
        "automatic_retry": False,
    }


def test_publish_envelope_redacts_payload_and_rejects_retry_semantics() -> None:
    envelope = _envelope()

    assert PRIVATE_AUTHORITY not in repr(envelope)
    assert PRIVATE_AUTHORITY not in str(envelope.as_dict())
    with pytest.raises(ValueError, match="one-shot contract"):
        OneShotPublishEnvelope(
            operation_key=envelope.operation_key,
            topic=envelope.topic,
            payload=envelope.payload,
            payload_sha256=envelope.payload_sha256,
            payload_bytes=envelope.payload_bytes,
            qos=1,
            retain=False,
        )
