from __future__ import annotations

import json
import sqlite3
import sys
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
import gateway


def test_authority_envelope_is_advisory_and_forbids_side_effects() -> None:
    env = gateway.build_envelope(
        service="classifier",
        operation="classify",
        mode="shadow",
        result={"labels": {"workflow_category": {"value": "devops"}}},
        npu_busy_delta_us=123,
        input_scope="explicit_text",
    )

    assert env["ok"] is True
    assert env["mode"] == "shadow"
    assert env["authority"] == {
        "may_route": False,
        "may_write_memory": False,
        "may_send_external": False,
        "may_process_private_dirs": False,
        "may_execute_tools": False,
        "may_restart_services": False,
    }
    assert env["npu_proof"] == {"required": True, "ok": True, "npu_busy_delta_us": 123}


def test_bind_host_requires_explicit_docker_bridge_approval() -> None:
    gateway.validate_bind_host("127.0.0.1")

    with pytest.raises(ValueError, match="without --allow-docker-bridge"):
        gateway.validate_bind_host("172.19.0.1")

    gateway.validate_bind_host("172.19.0.1", allow_docker_bridge=True)

    with pytest.raises(ValueError, match="approved bridge IP"):
        gateway.validate_bind_host("0.0.0.0", allow_docker_bridge=True)


def test_classify_calls_sidecar_and_logs_metadata_only(tmp_path: Path) -> None:
    calls: list[tuple[str, dict]] = []

    def fake_post(url: str, payload: dict, timeout_s: float) -> dict:
        calls.append((url, payload))
        return {
            "labels": {"tool_needed": {"value": True}},
            "npu_busy_delta_us": 55,
            "sysfs_npu_busy_delta_us": 55,
        }

    logger = gateway.AdvisoryLogger(tmp_path / "events.sqlite")
    env = gateway.classify_text(
        "Inspect live service status",
        trace_id="t1",
        http_post_json=fake_post,
        logger=logger,
    )

    assert calls[0][0].endswith(":18819/v1/classify")
    assert calls[0][1]["options"]["dry_run"] is True
    assert env["service"] == "classifier"
    assert env["authority"]["may_route"] is False
    assert env["npu_proof"]["ok"] is True

    with sqlite3.connect(tmp_path / "events.sqlite") as con:
        row = con.execute("select service, operation, input_ref, raw_payload from advisory_events").fetchone()
    assert row == ("classifier", "classify", "text:sha256:" + gateway.sha256_text("Inspect live service status"), None)


def test_generate_allows_only_bounded_jobs() -> None:
    with pytest.raises(ValueError, match="unsupported advisory generation job"):
        gateway.generate_bounded("primary_chat", "hello", http_post_json=lambda *_: {})


def test_generate_wraps_draft_without_final_authority() -> None:
    def fake_post(url: str, payload: dict, timeout_s: float) -> dict:
        return {"text": "Short title", "npu_busy_delta_us": 99, "timing_ms": {"total": 10}}

    env = gateway.generate_bounded("title", "Summarize this local health check", http_post_json=fake_post)

    assert env["service"] == "genai"
    assert env["operation"] == "generate:title"
    assert env["result"]["draft_text"] == "Short title"
    assert env["result"]["final_authority"] is False
    assert env["authority"]["may_send_external"] is False


def test_doc_triage_requires_explicit_file_under_allowed_root(tmp_path: Path) -> None:
    allowed = tmp_path / "allowed"
    allowed.mkdir()
    target = allowed / "synthetic.png"
    target.write_bytes(b"not real image for unit test")

    def fake_post(url: str, payload: dict, timeout_s: float) -> dict:
        assert payload["path"] == str(target.resolve())
        assert payload["options"]["allowed_roots"] == [str(allowed.resolve())]
        return {"ok": True, "result": {"pages": [{"needs_attention": {"embedding": {"verified_npu": True, "npu_busy_delta_us": 42}}}]}}

    env = gateway.triage_file(str(target), allowed_roots=[str(allowed)], configured_roots=[allowed], http_post_json=fake_post)

    assert env["service"] == "doc_triage"
    assert env["input_scope"] == "explicit_file"
    assert env["npu_proof"]["ok"] is True


def test_doc_triage_rejects_private_root_broadening(tmp_path: Path) -> None:
    allowed = tmp_path / "allowed"
    allowed.mkdir()
    with pytest.raises(ValueError, match="path must be inside an allowed root"):
        gateway.triage_file(str(tmp_path / "outside.png"), allowed_roots=[str(allowed)], configured_roots=[allowed], http_post_json=lambda *_: {})


def test_doc_triage_rejects_requested_root_outside_configured_roots(tmp_path: Path) -> None:
    configured = tmp_path / "configured"
    requested = tmp_path / "private"
    requested.mkdir()
    target = requested / "file.png"
    target.write_bytes(b"synthetic")

    with pytest.raises(ValueError, match="requested allowed root is outside configured roots"):
        gateway.triage_file(
            str(target),
            allowed_roots=[str(requested)],
            configured_roots=[configured],
            http_post_json=lambda *_: {},
        )


def test_health_aggregates_dependencies_without_raw_private_data() -> None:
    def fake_get(url: str, timeout_s: float) -> dict:
        return {"ok": True, "service": url.rsplit(":", 1)[-1]}

    health = gateway.health(http_get_json=fake_get)

    assert health["ok"] is True
    assert set(health["dependencies"]) == {"classifier", "genai", "doc_triage"}
    assert "raw" not in json.dumps(health).lower()
