"""Ehemals StagingGateway (zwei injizierte Policy-Callbacks, Anhänge pauschal
gesperrt). Neuer Vertrag: Gateway.handle() mit Policy-Matrix aus der
Konfiguration und HMAC-Clearance; Begründung je Test in docs/AENDERUNGEN.md."""
import hashlib
import unittest
from dataclasses import replace

from aria_router.adapter_contract import ResultStatus
from aria_router.adapters import MockAdapter
from aria_router.commands import CommandKind, parse_command
from aria_router.context import Attachment, ContextEnvelope, MemorySnippet
from aria_router.gateway import Handling

from tests.helpers import AdapterHarness, GatewayTestCase


class GatewayTests(GatewayTestCase):
    def setUp(self):
        super().setUp()
        self.mock = None

    def codex_factory(self, verifier):
        self.mock = MockAdapter(verifier, "codex", output="MOCK_OK")
        return self.mock

    def gw(self, config=None):
        return self.gateway(config, adapter_factories={"codex": self.codex_factory})

    def test_approved_mock(self):
        result = self.gw().handle(self.request(1, "#public hello"))
        self.assertEqual((result.status, result.reply_text), ("completed", "MOCK_OK"))
        self.assertEqual(self.mock.calls, 1)

    def test_no_approval(self):
        # Die Matrix gibt CONFIDENTIAL für kein Backend frei -> blockiert vor jeder Ausführung.
        gw = self.gw({"privacy": {"claude_status_quo_confidential": False}})
        result = gw.handle(self.request(2, "Angebot für BMW"))
        self.assertEqual(result.handling, Handling.BLOCKED)
        self.assertEqual(result.code, "PRIVACY_DENIED")
        self.assertEqual(self.mock.calls, 0)
        self.assertEqual(self.sink.delivered, [])

    def test_wrong_digest(self):
        harness = AdapterHarness(self.root / "h")
        adapter = MockAdapter(harness.verifier, "codex")
        job = harness.job("#public hello")
        clearance = harness.clear(adapter, job, context_digest="b" * 64)
        result = adapter.execute(clearance, job)
        self.assertEqual(result.error_code, "CONTEXT_DIGEST_MISMATCH")
        self.assertEqual(adapter.calls, 0)

    def test_memory_changes_digest(self):
        context = ContextEnvelope("hello", (MemorySnippet("brain/a.md", "memory-one"),))
        changed = replace(context, memory=(MemorySnippet("brain/a.md", "different"),))
        self.assertNotEqual(context.digest(), changed.digest())

    def test_attachments_blocked(self):
        # Anhänge ohne geprüfte Bytes sind nie PUBLIC -> nie an Codex; /codex wird abgelehnt.
        gw = self.gw()
        doc = Attachment(kind="document", file_id="BQAC1", name="doc.pdf", mime="application/pdf",
                         sha256=hashlib.sha256(b"doc").hexdigest())
        gw.handle(self.request(3, "/codex"))
        result = gw.handle(self.request(4, "#public hello", attachments=[doc]))
        self.assertEqual(result.handling, Handling.BLOCKED)
        self.assertEqual(result.code, "MANUAL_CHOICE_NOT_PERMITTED")
        self.assertEqual(self.mock.calls, 0)

    def test_confidential_blocked(self):
        gw = self.gw({"privacy": {"claude_status_quo_confidential": False}})
        result = gw.handle(self.request(5, "Kundendaten von adesso"))
        self.assertEqual(result.status, "blocked")
        self.assertEqual(self.mock.calls, 0)
        self.assertEqual(self.sink.delivered, [])

    def test_policy_exception_blocked(self):
        class BrokenMemory:
            def snippets_for(self, request, backend):
                raise RuntimeError("fail")
        gw = self.gateway(adapter_factories={"codex": self.codex_factory}, memory_provider=BrokenMemory())
        result = gw.handle(self.request(6, "#public hello"))
        self.assertEqual(result.code, "MEMORY_PROVIDER_ERROR")
        self.assertEqual(self.mock.calls, 0)

    def test_command_parsing(self):
        self.assertEqual(parse_command('/codex analyze').kind, CommandKind.ONE_SHOT)
        self.assertEqual(parse_command('/codex analyze').text, 'analyze')
        self.assertEqual(parse_command('/claude').kind, CommandKind.SET_MODE)
        self.assertEqual(parse_command('/auto').kind, CommandKind.SET_MODE)
        self.assertEqual(parse_command('/status').kind, CommandKind.PLUGIN_OWNED)
        self.assertEqual(parse_command('/status extra').kind, CommandKind.PLUGIN_OWNED)
        self.assertEqual(parse_command('hello').kind, CommandKind.MESSAGE)

    def test_gateway_backend_denied_before_execution(self):
        gw = self.gw({"privacy": {"matrix": {"codex": [], "claude": ["public", "internal", "unknown"]}}})
        result = gw.handle(self.request(7, "/codex #public hello"))
        self.assertEqual(result.handling, Handling.BLOCKED)
        self.assertEqual(result.code, "MANUAL_CHOICE_NOT_PERMITTED")
        self.assertEqual(self.mock.calls, 0)

    def test_invalid_attachment_digest(self):
        with self.assertRaises(ValueError):
            Attachment(kind="document", file_id="x", sha256="bad")


if __name__ == '__main__':
    unittest.main()
