from __future__ import annotations

from types import SimpleNamespace
from unittest.mock import AsyncMock, call, patch

import pytest


def _make_dispatch(**overrides):
    defaults = {
        "id": "dispatch-001",
        "governance_event_id": "event-001",
        "event_type": "knowledge_degradation",
        "executor_type": "hermes_kb_growth_healthcheck",
        "decision_context": {
            "workflow": {
                "impact": {
                    "stale_count": 1450,
                    "total_count": 1867,
                    "stale_ratio": 0.777,
                    "threshold": 0.2,
                    "stale_days": 7,
                },
                "stage_by_dispatch_status": {
                    "pending": "queued_kb_healthcheck",
                    "executing": "draft_km_updates",
                    "succeeded": "stale_ratio_recheck",
                },
            },
            "ownership": {
                "lead_agent": "Hermes",
                "human_owner": "KM owner / SRE owner",
            },
        },
    }
    defaults.update(overrides)
    return SimpleNamespace(**defaults)


def test_km_review_payload_is_review_only():
    """Hermes worker 只能產生 REVIEW 草稿，不可直接 approve/publish KM。"""
    from src.jobs.hermes_kb_growth_worker import _build_km_review_entry_payload
    from src.models.knowledge import EntrySource, EntryStatus, EntryType

    payload = _build_km_review_entry_payload(_make_dispatch())

    assert payload.entry_type == EntryType.AUTO_RUNBOOK
    assert payload.source == EntrySource.AI_EXTRACTED
    assert payload.status == EntryStatus.REVIEW
    assert "agent:Hermes" in payload.tags
    assert "needs_owner_review" in payload.tags
    assert "dispatch:dispatch-001" in payload.tags
    assert "governance_event:event-001" in payload.tags
    assert "writes_km_without_approval=false" in payload.content


def test_review_context_keeps_succeeded_at_owner_review_stage():
    """dispatch succeeded 代表 worker 完成草稿，不代表 KM 劣化已解決。"""
    from src.jobs.hermes_kb_growth_worker import _build_review_context

    context = _make_dispatch().decision_context
    updated = _build_review_context(
        context,
        dispatch_id="dispatch-001",
        governance_event_id="event-001",
        km_entry_id="km-001",
    )

    workflow = updated["workflow"]
    assert workflow["current_stage"] == "waiting_owner_review"
    assert workflow["stage_by_dispatch_status"]["succeeded"] == "waiting_owner_review"
    assert workflow["kb_draft_entry_id"] == "km-001"
    assert workflow["writes_km_without_approval"] is False
    assert updated["next_action"] == "owner_review_km_draft"
    assert updated["worker_result"]["status"] == "draft_created"


def test_knowledge_tag_filter_is_json_column_compatible():
    """knowledge_entries.tags 是 JSON 欄位時不可使用 json @> text。"""
    from src.repositories.knowledge_repository import _json_string_array_has_tag

    compiled = str(_json_string_array_has_tag("dispatch:abc-123"))

    assert "CAST(knowledge_entries.tags AS VARCHAR)" in compiled
    assert "@>" not in compiled


@pytest.mark.asyncio
async def test_run_once_advances_pending_dispatch_to_review_draft():
    """pending dispatch 應推進到 succeeded + waiting_owner_review read model。"""
    from src.jobs.hermes_kb_growth_worker import run_hermes_kb_growth_once

    pending = _make_dispatch()
    dispatched = _make_dispatch()
    executing = _make_dispatch()
    km_entry = SimpleNamespace(id="km-001")

    transition_mock = AsyncMock(side_effect=[dispatched, executing, _make_dispatch()])

    with (
        patch(
            "src.jobs.hermes_kb_growth_worker.list_pending_by_executor",
            new=AsyncMock(return_value=[pending]),
        ) as list_pending,
        patch(
            "src.jobs.hermes_kb_growth_worker.transition_status",
            new=transition_mock,
        ),
        patch(
            "src.jobs.hermes_kb_growth_worker._create_or_get_km_review_draft",
            new=AsyncMock(return_value=km_entry),
        ) as create_draft,
        patch(
            "src.jobs.hermes_kb_growth_worker.update_decision_context",
            new=AsyncMock(),
        ) as update_context,
    ):
        result = await run_hermes_kb_growth_once(limit=5)

    assert result == {"scanned": 1, "processed": 1, "skipped": 0, "failed": 0}
    list_pending.assert_awaited_once_with("hermes_kb_growth_healthcheck", limit=5)
    create_draft.assert_awaited_once_with(executing)
    transition_mock.assert_has_awaits([
        call("dispatch-001", "pending", "dispatched"),
        call("dispatch-001", "dispatched", "executing"),
        call("dispatch-001", "executing", "succeeded"),
    ])
    update_context.assert_awaited_once()
    updated_context = update_context.call_args.args[1]
    assert updated_context["workflow"]["current_stage"] == "waiting_owner_review"
    assert updated_context["worker_result"]["km_draft_entry_id"] == "km-001"
