"""Feedback-to-memory sync and operator surface for tool/workflow learning."""

from __future__ import annotations

import httpx
from click.testing import CliRunner
import pytest

from logos_adk.agent import Agent
from logos_adk.cli import cli
from logos_adk.learning import MemoryLearningEngine, SQLiteLearningStore, ToolWorkflowLearningEngine
from logos_adk.memory import LongTermMemory, MemoryEntry
from logos_adk.memory.backends import InMemoryBackend
from logos_adk.providers.mock import MockProvider


async def _request(app, method: str, path: str, **kwargs):
    transport = httpx.ASGITransport(app=app)
    async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client:
        return await client.request(method, path, **kwargs)


@pytest.mark.asyncio
async def test_feedback_endpoint_syncs_negative_feedback_into_memory_learning(tmp_path):
    from logos_adk.serve import create_app

    backend = InMemoryBackend()
    memory_learning = MemoryLearningEngine()
    entry = memory_learning.initialize_entry(
        MemoryEntry(
            content="VIP customers prefer concise, factual answers.",
            session_id="acme:lead-1",
        )
    )
    await backend.store(entry)

    agent = Agent("bot", model=MockProvider(responses=["draft answer"])).memory(
        LongTermMemory(backend=backend, retrieval="keyword", limit=1, learning=memory_learning)
    )
    learning_store = SQLiteLearningStore(str(tmp_path / "learning.db"))
    app = create_app(
        [agent],
        learning_store=learning_store,
        api_keys={
            "learn-key": {
                "name": "learner",
                "scopes": ["agent:run", "learning:write", "learning:read"],
                "workspaces": ["acme"],
            }
        },
        require_workspace=True,
    )

    run_response = await _request(
        app,
        "POST",
        "/run",
        json={"prompt": "Write a concise answer for a VIP customer", "session_id": "lead-1"},
        headers={
            "Authorization": "Bearer learn-key",
            "X-Workspace-Id": "acme",
            "X-Trace-Id": "trace-memory-1",
        },
    )
    assert run_response.status_code == 200
    run_payload = run_response.json()
    run_record = learning_store.get_run(run_payload["run_id"])
    assert run_record is not None
    assert run_record.metadata["memory_entry_ids"] == [entry.id]

    feedback_response = await _request(
        app,
        "POST",
        "/feedback",
        json={
            "run_id": run_payload["run_id"],
            "kind": "thumbs_down",
            "failure_types": ["style_miss"],
        },
        headers={
            "Authorization": "Bearer learn-key",
            "X-Workspace-Id": "acme",
        },
    )
    assert feedback_response.status_code == 200

    synced_entries = await backend.search("", limit=10, session_id="acme:lead-1")
    synced = next(item for item in synced_entries if item.id == entry.id)
    assert synced.id == entry.id
    assert synced.harmful_count == 1
    assert synced.metadata["last_feedback_reason"] == "feedback:thumbs_down:style_miss"


@pytest.mark.asyncio
async def test_tool_workflow_operator_http_and_cli_surface(tmp_path):
    from logos_adk.serve import create_app

    engine = ToolWorkflowLearningEngine(path=str(tmp_path / "tool_workflow.db"))
    engine.record_tool_call(
        tool_name="search_docs",
        success=True,
        duration_ms=12,
        workspace_id="acme",
        task_type="support",
    )
    engine.record_tool_call(
        tool_name="web_lookup",
        success=False,
        duration_ms=40,
        workspace_id="acme",
        task_type="support",
    )
    engine.record_fallback(
        provider_key="MockProvider:backup",
        success=True,
        duration_ms=22,
        workspace_id="acme",
        task_type="support",
    )
    engine.record_workflow_branch(
        pipeline_name="refunds",
        step_name="routing",
        branch_name="approve_path",
        success=True,
        duration_ms=18,
        workspace_id="acme",
    )
    engine.record_loop_execution(
        pipeline_name="refunds",
        step_name="draft_loop",
        iterations=5,
        max_iterations=5,
        termination_reason="max_iterations",
        success=True,
        workspace_id="acme",
    )
    engine.record_loop_execution(
        pipeline_name="refunds",
        step_name="draft_loop",
        iterations=4,
        max_iterations=5,
        termination_reason="condition_met",
        success=True,
        workspace_id="acme",
    )
    engine.record_approval_request(
        pipeline_name="refunds",
        step_name="review",
        workspace_id="acme",
    )
    engine.record_approval_outcome(
        pipeline_name="refunds",
        step_name="review",
        approved=False,
        corrected=True,
        workspace_id="acme",
    )

    agent = Agent("bot", model=MockProvider(responses=["ok"])).tool_workflow_learning(engine)
    app = create_app([agent])

    tool_scores = await _request(
        app,
        "GET",
        "/learning/tool-workflow/tool-scores?workspace_id=acme&task_type=support",
    )
    fallback_scores = await _request(
        app,
        "GET",
        "/learning/tool-workflow/fallback-rankings?workspace_id=acme&task_type=support",
    )
    approval_hotspots = await _request(
        app,
        "GET",
        "/learning/tool-workflow/approval-hotspots?workspace_id=acme&pipeline_name=refunds",
    )
    branch_scores = await _request(
        app,
        "GET",
        "/learning/tool-workflow/branch-scores?workspace_id=acme&pipeline_name=refunds",
    )
    loop_recommendations = await _request(
        app,
        "GET",
        "/learning/tool-workflow/loop-recommendations?workspace_id=acme&pipeline_name=refunds",
    )

    assert tool_scores.status_code == 200
    assert fallback_scores.status_code == 200
    assert approval_hotspots.status_code == 200
    assert branch_scores.status_code == 200
    assert loop_recommendations.status_code == 200

    assert tool_scores.json()["items"][0]["tool_name"] == "search_docs"
    assert fallback_scores.json()["items"][0]["provider_key"] == "MockProvider:backup"
    assert approval_hotspots.json()["items"][0]["step_name"] == "review"
    assert approval_hotspots.json()["items"][0]["correction_rate"] == pytest.approx(1.0)
    assert branch_scores.json()["items"][0]["branch_name"] == "approve_path"
    assert loop_recommendations.json()["items"][0]["step_name"] == "draft_loop"
    assert loop_recommendations.json()["items"][0]["recommended_limit"] >= 5

    runner = CliRunner()
    tool_result = runner.invoke(
        cli,
        [
            "tool-workflow",
            "tool-scores",
            "--db-path",
            str(tmp_path / "tool_workflow.db"),
            "--workspace-id",
            "acme",
            "--task-type",
            "support",
        ],
    )
    approval_result = runner.invoke(
        cli,
        [
            "tool-workflow",
            "approval-hotspots",
            "--db-path",
            str(tmp_path / "tool_workflow.db"),
            "--workspace-id",
            "acme",
            "--pipeline-name",
            "refunds",
        ],
    )
    loop_result = runner.invoke(
        cli,
        [
            "tool-workflow",
            "loop-recommendations",
            "--db-path",
            str(tmp_path / "tool_workflow.db"),
            "--workspace-id",
            "acme",
            "--pipeline-name",
            "refunds",
        ],
    )

    assert tool_result.exit_code == 0
    assert "search_docs" in tool_result.output
    assert approval_result.exit_code == 0
    assert "refunds/review" in approval_result.output
    assert loop_result.exit_code == 0
    assert "draft_loop" in loop_result.output
