"""Integration tests for AI Flow Builder endpoints.

All AI provider calls are mocked — zero real API spend.
"""
import json
from unittest.mock import AsyncMock, patch, MagicMock

import pytest

from app.core.config import settings


# ── Sample AI responses ──

SCAFFOLD_RESPONSE_JSON = json.dumps({
    "branches": [
        {"name": "Service Not Running", "description": "The target service is stopped or crashed."},
        {"name": "Authentication Failures", "description": "Users cannot authenticate against the service."},
        {"name": "Network Connectivity", "description": "Network-level issues preventing access."},
        {"name": "Configuration Errors", "description": "Misconfiguration of the service or its dependencies."},
    ]
})

BRANCH_DETAIL_JSON = json.dumps({
    "id": "svc-root",
    "type": "decision",
    "question": "Is the service running?",
    "options": [
        {"id": "opt-yes", "label": "Yes", "next_node_id": "svc-check-logs"},
        {"id": "opt-no", "label": "No", "next_node_id": "svc-restart"},
    ],
    "children": [
        {
            "id": "svc-check-logs",
            "type": "action",
            "title": "Check Event Logs",
            "description": "Check Windows Event Viewer for errors.",
            "commands": ["Get-EventLog -LogName Application -Newest 20"],
            "next_node_id": "svc-logs-resolved",
        },
        {
            "id": "svc-logs-resolved",
            "type": "solution",
            "title": "Issue Found in Logs",
            "description": "Error identified and resolved.",
            "resolution_steps": ["Fix the error", "Restart service"],
        },
        {
            "id": "svc-restart",
            "type": "action",
            "title": "Restart Service",
            "description": "Attempt to restart the service.",
            "commands": ["Restart-Service -Name 'TestService'"],
            "next_node_id": "svc-restart-ok",
        },
        {
            "id": "svc-restart-ok",
            "type": "solution",
            "title": "Service Restored",
            "description": "Service is running after restart.",
            "resolution_steps": ["Verify connectivity", "Document in ticket"],
        },
    ],
})


def _mock_ai_provider(text: str, input_tokens: int = 100, output_tokens: int = 200):
    """Create a mock AI provider whose generate_json returns the given text and token counts."""
    provider = MagicMock()
    provider.generate_json = AsyncMock(return_value=(text, input_tokens, output_tokens))
    return provider


@pytest.fixture
def enable_ai():
    """Temporarily enable AI by setting a fake API key."""
    original = settings.ANTHROPIC_API_KEY
    settings.ANTHROPIC_API_KEY = "test-key-fake"
    yield
    settings.ANTHROPIC_API_KEY = original


@pytest.fixture
def disable_ai():
    """Ensure AI is disabled."""
    original = settings.ANTHROPIC_API_KEY
    settings.ANTHROPIC_API_KEY = None
    yield
    settings.ANTHROPIC_API_KEY = original


# ── Quota endpoint ──


@pytest.mark.asyncio
async def test_quota_returns_disabled_when_no_key(client, auth_headers, disable_ai):
    """GET /ai/quota returns ai_enabled=false when no API key."""
    response = await client.get("/api/v1/ai/quota", headers=auth_headers)
    assert response.status_code == 200
    data = response.json()
    assert data["ai_enabled"] is False
    assert data["allowed"] is False


@pytest.mark.asyncio
async def test_quota_returns_enabled_with_key(client, auth_headers, enable_ai):
    """GET /ai/quota returns ai_enabled=true with API key configured."""
    response = await client.get("/api/v1/ai/quota", headers=auth_headers)
    assert response.status_code == 200
    data = response.json()
    assert data["ai_enabled"] is True
    assert data["allowed"] is True


# ── Start endpoint ──


@pytest.mark.asyncio
async def test_start_requires_auth(client, enable_ai):
    """POST /ai/start requires authentication."""
    response = await client.post("/api/v1/ai/start", json={
        "flow_type": "troubleshooting",
        "name": "Test Flow",
        "description": "Test",
    })
    assert response.status_code == 401


@pytest.mark.asyncio
async def test_start_returns_503_when_disabled(client, auth_headers, disable_ai):
    """POST /ai/start returns 503 when AI is not configured."""
    response = await client.post(
        "/api/v1/ai/start",
        json={
            "flow_type": "troubleshooting",
            "name": "Test Flow",
            "description": "Test description",
        },
        headers=auth_headers,
    )
    assert response.status_code == 503


@pytest.mark.asyncio
async def test_start_creates_conversation(client, auth_headers, enable_ai):
    """POST /ai/start creates a conversation and returns conversation_id."""
    response = await client.post(
        "/api/v1/ai/start",
        json={
            "flow_type": "troubleshooting",
            "name": "DNS Issues",
            "description": "Troubleshooting DNS resolution failures",
            "environment_tags": ["Windows Server", "Active Directory"],
        },
        headers=auth_headers,
    )
    assert response.status_code == 201
    data = response.json()
    assert "conversation_id" in data
    assert data["status"] == "foundation"


@pytest.mark.asyncio
async def test_start_validates_input(client, auth_headers, enable_ai):
    """POST /ai/start rejects invalid input."""
    response = await client.post(
        "/api/v1/ai/start",
        json={
            "flow_type": "troubleshooting",
            "name": "",  # Empty name
            "description": "Test",
        },
        headers=auth_headers,
    )
    assert response.status_code == 422


# ── Scaffold endpoint ──


@pytest.mark.asyncio
async def test_scaffold_success(client, auth_headers, enable_ai):
    """POST /ai/scaffold returns AI-generated branches."""
    # Create conversation first
    start_resp = await client.post(
        "/api/v1/ai/start",
        json={
            "flow_type": "troubleshooting",
            "name": "DNS Issues",
            "description": "DNS resolution failures",
        },
        headers=auth_headers,
    )
    conversation_id = start_resp.json()["conversation_id"]

    # Mock AI provider
    mock_provider = _mock_ai_provider(SCAFFOLD_RESPONSE_JSON)
    with patch("app.core.ai_tree_generator_service.get_ai_provider", return_value=mock_provider):
        response = await client.post(
            "/api/v1/ai/scaffold",
            json={"conversation_id": conversation_id},
            headers=auth_headers,
        )

    assert response.status_code == 200
    data = response.json()
    assert data["status"] == "scaffolding"
    assert len(data["branches"]) == 4
    assert data["branches"][0]["name"] == "Service Not Running"


@pytest.mark.asyncio
async def test_scaffold_invalid_conversation(client, auth_headers, enable_ai):
    """POST /ai/scaffold returns 404 for nonexistent conversation."""
    response = await client.post(
        "/api/v1/ai/scaffold",
        json={"conversation_id": "00000000-0000-0000-0000-000000000000"},
        headers=auth_headers,
    )
    assert response.status_code == 404


# ── Branch detail endpoint ──


@pytest.mark.asyncio
async def test_branch_detail_success(client, auth_headers, enable_ai):
    """POST /ai/branch-detail returns AI-generated branch nodes."""
    # Create and scaffold first
    start_resp = await client.post(
        "/api/v1/ai/start",
        json={
            "flow_type": "troubleshooting",
            "name": "Service Issues",
            "description": "Service troubleshooting",
        },
        headers=auth_headers,
    )
    conversation_id = start_resp.json()["conversation_id"]

    scaffold_provider = _mock_ai_provider(SCAFFOLD_RESPONSE_JSON)
    with patch("app.core.ai_tree_generator_service.get_ai_provider", return_value=scaffold_provider):
        await client.post(
            "/api/v1/ai/scaffold",
            json={"conversation_id": conversation_id},
            headers=auth_headers,
        )

    # Now generate branch detail
    detail_provider = _mock_ai_provider(BRANCH_DETAIL_JSON)
    with patch("app.core.ai_tree_generator_service.get_ai_provider", return_value=detail_provider):
        response = await client.post(
            "/api/v1/ai/branch-detail",
            json={
                "conversation_id": conversation_id,
                "branch_name": "Service Not Running",
            },
            headers=auth_headers,
        )

    assert response.status_code == 200
    data = response.json()
    assert data["branch_name"] == "Service Not Running"
    assert data["steps"]["id"] == "svc-root"
    assert data["steps"]["type"] == "decision"


# ── Assemble endpoint ──


@pytest.mark.asyncio
async def test_assemble_success(client, auth_headers, enable_ai):
    """POST /ai/assemble returns assembled tree from branches with detail."""
    # Create conversation
    start_resp = await client.post(
        "/api/v1/ai/start",
        json={
            "flow_type": "troubleshooting",
            "name": "Service Issues",
            "description": "Service troubleshooting",
        },
        headers=auth_headers,
    )
    conversation_id = start_resp.json()["conversation_id"]

    # Scaffold
    scaffold_provider = _mock_ai_provider(SCAFFOLD_RESPONSE_JSON)
    with patch("app.core.ai_tree_generator_service.get_ai_provider", return_value=scaffold_provider):
        await client.post(
            "/api/v1/ai/scaffold",
            json={"conversation_id": conversation_id},
            headers=auth_headers,
        )

    # Assemble with branch detail included
    branch_tree = json.loads(BRANCH_DETAIL_JSON)
    response = await client.post(
        "/api/v1/ai/assemble",
        json={
            "conversation_id": conversation_id,
            "selected_branches": [
                {
                    "name": "Service Not Running",
                    "description": "The target service is stopped.",
                    "steps": branch_tree,
                },
                {
                    "name": "Authentication Failures",
                    "description": "Users cannot authenticate.",
                    "steps": branch_tree,
                },
            ],
        },
        headers=auth_headers,
    )

    assert response.status_code == 200
    data = response.json()
    assert data["status"] == "completed"
    assert data["suggested_name"] == "Service Issues"
    assert "tree_structure" in data
    assert data["tree_structure"]["type"] == "decision"
    assert data["summary"]["node_count"] > 0
    assert data["summary"]["solution_count"] >= 2


@pytest.mark.asyncio
async def test_assemble_requires_min_2_branches(client, auth_headers, enable_ai):
    """POST /ai/assemble rejects fewer than 2 branches."""
    start_resp = await client.post(
        "/api/v1/ai/start",
        json={
            "flow_type": "troubleshooting",
            "name": "Test",
            "description": "Test",
        },
        headers=auth_headers,
    )
    conversation_id = start_resp.json()["conversation_id"]

    response = await client.post(
        "/api/v1/ai/assemble",
        json={
            "conversation_id": conversation_id,
            "selected_branches": [
                {"name": "Only Branch", "description": "Just one"},
            ],
        },
        headers=auth_headers,
    )
    assert response.status_code == 422
