from __future__ import annotations

import hashlib
import importlib
import os
import sys
from pathlib import Path
from typing import Any, Dict, List

from api.drawer_firebreak import DrawerRuntime
from hats.manifest import HatDrawer, HatManifest


class _TemporalStub:
    def __init__(self):
        self._kv: Dict[str, Any] = {}

    def get(self, key: str):
        return self._kv.get(key)

    def set(self, key: str, value: Any):
        self._kv[key] = value
        return True

    def delete(self, key: str):
        if key in self._kv:
            del self._kv[key]
            return True
        return False

    def keys(self, pattern: str = "*") -> List[str]:
        if pattern == "*":
            return sorted(self._kv.keys())
        import re

        sql_like = pattern.replace("*", ".*")
        regex = re.compile("^" + sql_like + "$")
        return sorted(k for k in self._kv.keys() if regex.match(k))


class _StoreStub:
    def __init__(self):
        self.temporal = _TemporalStub()

    def query(self, *args, **kwargs):
        return []


def test_phase4_manifest_schema_contract_roundtrip():
    manifest = HatManifest(
        id="tmpl_test_hat",
        name="Test Hat",
        version="1.2.0",
        domain="testing",
        drawers=[
            HatDrawer(
                drawer_id="receipt_security",
                type="fact",
                description="Receipt constraints",
                index_weight=0.9,
                briefing_priority=0.8,
                promotion_policy="fact_strict",
                sensitive=False,
            )
        ],
    )
    errors = manifest.validate()
    assert not errors

    payload = manifest.to_dict()
    reparsed = HatManifest.from_dict(payload)
    contract = reparsed.schema_contract()
    assert contract["hat_template_id"] == "tmpl_test_hat"
    assert contract["drawers"][0]["drawer_id"] == "receipt_security"
    assert contract["template_version"] == reparsed.template_version


def test_phase4_runtime_state_counters_and_schema_payload(tmp_path):
    runtime = DrawerRuntime(store=_StoreStub(), hat_instance_id="hat_phase4_product")

    result = runtime.ingest_batch(
        events=[
            {
                "event_id": "evt1",
                "event_type": "directive_set",
                "directive_id": "dir1",
                "content": "Stay deterministic",
                "timestamp": "2026-02-28T19:00:00Z",
            },
            {
                "event_id": "evt2",
                "event_type": "observation",
                "observation": "raw trail",
                "timestamp": "2026-02-28T19:01:00Z",
            },
        ],
        agent_id="codex",
        session_id="sess_phase4",
    )
    assert result["accepted"] == 2

    status = runtime.status_snapshot()
    runtime_state = status["state"]["runtime_state"]
    assert runtime_state["total_writebacks"] == 2
    assert runtime_state["total_batches"] == 1
    assert "codex" in runtime_state["active_agents"]
    assert any(row["drawer_id"] == "directives" and row["confirmed_count"] >= 1 for row in runtime_state["drawer_stats"])

    # Validate API helper returns schema/runtime payload for unregistered hats.
    os.environ["MEMORY_DATA_DIR"] = str(tmp_path / "tenant_data")
    if "api.main" in sys.modules:
        del sys.modules["api.main"]
    api_main = importlib.import_module("api.main")
    schema_payload = api_main._hat_schema_payload(api_key="phase4_test_key", hat_id="hat_unregistered")
    assert schema_payload["hat_instance_id"] == "hat_unregistered"
    assert schema_payload["source"] in {"runtime_default", "manifest"}
    assert "drawers" in schema_payload["hat_template"]
    assert "runtime_state" in schema_payload


def test_phase4_admin_migration_report_payload(tmp_path):
    base_dir = tmp_path / "tenant_data"
    tenant_dir = base_dir / "tenants" / "tenant_phase4_admin"
    tenant_dir.mkdir(parents=True, exist_ok=True)

    from scripts.drawer_migration_lib import SQLiteTemporalStore

    temporal = SQLiteTemporalStore(db_path=str(tenant_dir / "temporal.db"))
    try:
        temporal.set(
            "memory:admin-1",
            {
                "id": "admin-1",
                "content": "Migration admin payload test",
                "context": "knowledge_admin",
                "importance": 0.8,
                "created_at": "2026-02-28T21:00:00Z",
            },
        )
    finally:
        temporal.close()

    os.environ["MEMORY_DATA_DIR"] = str(base_dir)
    if "api.main" in sys.modules:
        del sys.modules["api.main"]
    api_main = importlib.import_module("api.main")

    payload = api_main._admin_migration_report_payload(
        verify_preservation=99.9,
        emit_checksums=False,
        tenant_filters=["tenant_phase4_admin"],
        dry_run=True,
    )
    assert payload["status"] == "DRY_RUN_OK"
    assert payload["global"]["pre_total_records"] == 1
    assert payload["global"]["verify_preservation_passed"] is True
    assert "pre_checksums" not in payload["global"]
    assert payload["meta"]["base_data_dir"] == str(base_dir)


def test_phase4_tenant_migration_report_payload_scope(tmp_path):
    base_dir = tmp_path / "tenant_data"
    own_api_key = "mnx_phase4_tenant_key"
    own_tenant = hashlib.sha256(own_api_key.encode()).hexdigest()[:16]
    other_tenant = "ffffffffffffffff"

    from scripts.drawer_migration_lib import SQLiteTemporalStore

    own_dir = base_dir / "tenants" / own_tenant
    other_dir = base_dir / "tenants" / other_tenant
    own_dir.mkdir(parents=True, exist_ok=True)
    other_dir.mkdir(parents=True, exist_ok=True)

    own_store = SQLiteTemporalStore(db_path=str(own_dir / "temporal.db"))
    own_store.set(
        "memory:own-1",
        {
            "id": "own-1",
            "content": "Own tenant memory",
            "context": "knowledge_own",
            "importance": 0.9,
            "created_at": "2026-02-28T22:10:00Z",
        },
    )
    own_store.close()

    other_store = SQLiteTemporalStore(db_path=str(other_dir / "temporal.db"))
    other_store.set(
        "memory:other-1",
        {
            "id": "other-1",
            "content": "Other tenant memory",
            "context": "knowledge_other",
            "importance": 0.5,
            "created_at": "2026-02-28T22:11:00Z",
        },
    )
    other_store.close()

    os.environ["MEMORY_DATA_DIR"] = str(base_dir)
    if "api.main" in sys.modules:
        del sys.modules["api.main"]
    api_main = importlib.import_module("api.main")
    api_main._api_keys[own_api_key] = {
        "name": "tenant-report-test",
        "created": "2026-02-28T00:00:00Z",
        "calls": 0,
        "tier": "solo",
    }

    payload = api_main._tenant_migration_report_payload(
        api_key=own_api_key,
        verify_preservation=99.9,
        emit_checksums=False,
        dry_run=True,
    )
    assert payload["status"] == "DRY_RUN_OK"
    assert payload["tenants_scanned"] == 1
    assert len(payload["tenants"]) == 1
    assert payload["tenants"][0]["tenant_id"] == own_tenant
    assert payload["global"]["pre_total_records"] == 1
    assert payload["meta"]["scope"] == "tenant"
    assert payload["meta"]["tenant_id"] == own_tenant
    assert "base_data_dir" not in payload["meta"]
