"""Integration tests for KB Accelerator endpoints."""
import pytest
import json
from unittest.mock import AsyncMock, patch, PropertyMock
from httpx import AsyncClient

pytestmark = pytest.mark.asyncio


# ── Fixtures ──


@pytest.fixture
async def kb_setup(client, auth_headers, test_db):
    """Seed KB plan limits and return helpers."""
    # Update plan_limits with KB columns for 'free' plan
    await test_db.execute(
        __import__("sqlalchemy").text("""
            UPDATE plan_limits SET
                kb_accelerator_enabled = true,
                kb_max_lifetime_conversions = 3,
                kb_allowed_formats = '["txt","paste"]'::jsonb,
                kb_detailed_analysis = false,
                kb_conversational_refinement = false,
                kb_step_library_matching = false,
                kb_history_limit = 3
            WHERE plan = 'free'
        """)
    )
    await test_db.execute(
        __import__("sqlalchemy").text("""
            UPDATE plan_limits SET
                kb_accelerator_enabled = true,
                kb_max_lifetime_conversions = NULL,
                kb_allowed_formats = '["txt","paste","docx","pdf","html","md"]'::jsonb,
                kb_detailed_analysis = true,
                kb_conversational_refinement = true,
                kb_step_library_matching = true,
                kb_history_limit = NULL
            WHERE plan = 'pro'
        """)
    )
    await test_db.commit()
    return {"client": client, "headers": auth_headers}


def _mock_ai_enabled():
    """Context manager to mock AI as enabled."""
    return patch.object(
        type(__import__("app.core.config", fromlist=["settings"]).settings),
        "ai_enabled",
        new_callable=PropertyMock,
        return_value=True,
    )


SAMPLE_KB_TEXT = """
Troubleshooting Outlook Connectivity Issues

Problem: Users report that Outlook keeps disconnecting from Exchange.

Step 1: Check Network Connectivity
Ping the Exchange server to verify network connectivity.
If ping fails, check the network configuration.

Step 2: Verify Outlook Profile
If the network is working, check the Outlook profile settings.
Go to Control Panel > Mail > Show Profiles.

Step 3: Check Exchange Server
If the profile is correct, verify the Exchange server is running.
Open Services.msc and check Microsoft Exchange services.

Resolution: After following these steps, Outlook should maintain
a persistent connection to Exchange.
"""

MOCK_AI_TROUBLESHOOTING_RESPONSE = json.dumps({
    "title": "Troubleshooting Outlook Connectivity",
    "description": "Diagnose and fix Outlook disconnection from Exchange",
    "nodes": [
        {
            "id": "root-check",
            "type": "question",
            "question": "Is the network connection working?",
            "options": [
                {"label": "Yes", "next_node_id": "check-profile"},
                {"label": "No", "next_node_id": "fix-network"},
            ],
            "confidence": 0.92,
            "source_excerpt": "Step 1: Check Network Connectivity",
        },
        {
            "id": "fix-network",
            "type": "resolution",
            "question": "Fix the network configuration and retry.",
            "confidence": 0.85,
            "source_excerpt": "If ping fails, check the network configuration.",
        },
        {
            "id": "check-profile",
            "type": "question",
            "question": "Is the Outlook profile configured correctly?",
            "options": [
                {"label": "Yes", "next_node_id": "check-exchange"},
                {"label": "No", "next_node_id": "fix-profile"},
            ],
            "confidence": 0.88,
            "source_excerpt": "Step 2: Verify Outlook Profile",
        },
        {
            "id": "fix-profile",
            "type": "resolution",
            "question": "Reconfigure the Outlook profile via Control Panel > Mail.",
            "confidence": 0.90,
            "source_excerpt": "Go to Control Panel > Mail > Show Profiles.",
        },
        {
            "id": "check-exchange",
            "type": "resolution",
            "question": "Verify Exchange services are running in Services.msc.",
            "confidence": 0.87,
            "source_excerpt": "Open Services.msc and check Microsoft Exchange services.",
        },
    ],
})

MOCK_AI_PROCEDURAL_RESPONSE = json.dumps({
    "title": "Setup New Domain Controller",
    "description": "Step-by-step procedure for setting up a new DC",
    "steps": [
        {
            "id": "step-1",
            "type": "step",
            "content": "Open Server Manager on [VAR:server_name]",
            "confidence": 0.95,
            "source_excerpt": "Step 1: Open Server Manager on DC01",
        },
        {
            "id": "warning-dns",
            "type": "warning",
            "content": "WARNING: This will restart DNS and cause brief connectivity loss",
            "confidence": 0.90,
            "source_excerpt": "Note: Restarting DNS will cause a brief outage",
        },
        {
            "id": "step-2",
            "type": "step",
            "content": "Configure IP address [VAR:ip_address] on the network adapter",
            "confidence": 0.88,
            "source_excerpt": "Configure IP 192.168.1.10 on the adapter",
        },
    ],
    "intake_form": [
        {
            "variable_name": "server_name",
            "label": "Server Name",
            "field_type": "text",
            "required": True,
            "display_order": 1,
        },
        {
            "variable_name": "ip_address",
            "label": "IP Address",
            "field_type": "text",
            "required": True,
            "display_order": 2,
        },
    ],
})


# ── Upload Tests ──


class TestUpload:
    async def test_upload_text_paste(self, kb_setup):
        """Upload via text paste creates a kb_import in processing status."""
        c, h = kb_setup["client"], kb_setup["headers"]

        with _mock_ai_enabled():
            # Mock the background conversion (don't actually call AI)
            with patch("app.api.endpoints.kb_accelerator._run_conversion"):
                resp = await c.post(
                    "/api/v1/kb-accelerator/upload",
                    data={"content": SAMPLE_KB_TEXT, "target_type": "troubleshooting"},
                    headers=h,
                )

        assert resp.status_code == 201
        data = resp.json()
        assert data["status"] == "processing"
        assert data["source_format"] == "paste"
        assert "id" in data

    async def test_upload_empty_content_rejected(self, kb_setup):
        c, h = kb_setup["client"], kb_setup["headers"]
        with _mock_ai_enabled():
            resp = await c.post(
                "/api/v1/kb-accelerator/upload",
                data={"content": "short"},
                headers=h,
            )
        assert resp.status_code == 400

    async def test_upload_no_file_no_content_rejected(self, kb_setup):
        c, h = kb_setup["client"], kb_setup["headers"]
        with _mock_ai_enabled():
            resp = await c.post(
                "/api/v1/kb-accelerator/upload",
                data={},
                headers=h,
            )
        assert resp.status_code == 400


# ── Get/List Tests ──


class TestGetList:
    async def test_get_import(self, kb_setup):
        c, h = kb_setup["client"], kb_setup["headers"]

        with _mock_ai_enabled(), patch("app.api.endpoints.kb_accelerator._run_conversion"):
            create_resp = await c.post(
                "/api/v1/kb-accelerator/upload",
                data={"content": SAMPLE_KB_TEXT, "target_type": "troubleshooting"},
                headers=h,
            )
        import_id = create_resp.json()["id"]

        resp = await c.get(f"/api/v1/kb-accelerator/{import_id}", headers=h)
        assert resp.status_code == 200
        data = resp.json()
        assert data["id"] == import_id
        assert data["source_format"] == "paste"

    async def test_list_imports(self, kb_setup):
        c, h = kb_setup["client"], kb_setup["headers"]

        with _mock_ai_enabled(), patch("app.api.endpoints.kb_accelerator._run_conversion"):
            await c.post(
                "/api/v1/kb-accelerator/upload",
                data={"content": SAMPLE_KB_TEXT, "target_type": "troubleshooting"},
                headers=h,
            )

        resp = await c.get("/api/v1/kb-accelerator", headers=h)
        assert resp.status_code == 200
        data = resp.json()
        assert data["total"] >= 1
        assert len(data["items"]) >= 1


# ── Quota Tests ──


class TestQuota:
    async def test_get_quota(self, kb_setup):
        c, h = kb_setup["client"], kb_setup["headers"]
        resp = await c.get("/api/v1/kb-accelerator/quota", headers=h)
        assert resp.status_code == 200
        data = resp.json()
        assert data["kb_accelerator_enabled"] is True
        assert data["lifetime_conversions_limit"] == 3
        assert data["can_convert"] is True


# ── Commit Tests ──


class TestCommit:
    async def test_commit_creates_tree(self, kb_setup, test_db):
        """Committing a ready import creates a Tree record."""
        c, h = kb_setup["client"], kb_setup["headers"]

        # Create import
        with _mock_ai_enabled(), patch("app.api.endpoints.kb_accelerator._run_conversion"):
            create_resp = await c.post(
                "/api/v1/kb-accelerator/upload",
                data={"content": SAMPLE_KB_TEXT, "target_type": "troubleshooting"},
                headers=h,
            )
        import_id = create_resp.json()["id"]

        # Simulate conversion complete: update status + add nodes directly
        from app.models.kb_import import KBImport, KBImportNode
        from sqlalchemy import select
        import uuid

        result = await test_db.execute(select(KBImport).where(KBImport.id == uuid.UUID(import_id)))
        kb_import = result.scalar_one()
        kb_import.status = "ready"
        kb_import.source_metadata = {"_conversion": {"title": "Test Flow", "description": "Test"}}

        # Build a valid tree: root decision with 2 branches leading to solutions
        nodes_data = [
            KBImportNode(
                kb_import_id=kb_import.id, node_order=0, node_type="question",
                content={
                    "original_id": "root", "question": "What is the issue?",
                    "options": [
                        {"id": "opt-root-0", "label": "Option A", "next_node_id": "action-a"},
                        {"id": "opt-root-1", "label": "Option B", "next_node_id": "action-b"},
                    ],
                },
                confidence_score=0.9,
            ),
            KBImportNode(
                kb_import_id=kb_import.id, node_order=1, node_type="action",
                content={"original_id": "action-a", "question": "Try fix A", "description": "Do thing A", "next_node_id": "solution-a"},
                confidence_score=0.9,
            ),
            KBImportNode(
                kb_import_id=kb_import.id, node_order=2, node_type="action",
                content={"original_id": "action-b", "question": "Try fix B", "description": "Do thing B", "next_node_id": "solution-b"},
                confidence_score=0.9,
            ),
            KBImportNode(
                kb_import_id=kb_import.id, node_order=3, node_type="resolution",
                content={"original_id": "solution-a", "question": "Resolved via A", "description": "Issue fixed by A"},
                confidence_score=0.9,
            ),
            KBImportNode(
                kb_import_id=kb_import.id, node_order=4, node_type="resolution",
                content={"original_id": "solution-b", "question": "Resolved via B", "description": "Issue fixed by B"},
                confidence_score=0.9,
            ),
        ]
        for n in nodes_data:
            test_db.add(n)
        await test_db.commit()

        # Commit
        resp = await c.post(f"/api/v1/kb-accelerator/{import_id}/commit", headers=h)
        assert resp.status_code == 200
        data = resp.json()
        assert "tree_id" in data
        assert data["tree_type"] == "troubleshooting"


# ── Delete Tests ──


class TestDelete:
    async def test_delete_import(self, kb_setup):
        c, h = kb_setup["client"], kb_setup["headers"]

        with _mock_ai_enabled(), patch("app.api.endpoints.kb_accelerator._run_conversion"):
            create_resp = await c.post(
                "/api/v1/kb-accelerator/upload",
                data={"content": SAMPLE_KB_TEXT, "target_type": "troubleshooting"},
                headers=h,
            )
        import_id = create_resp.json()["id"]

        resp = await c.delete(f"/api/v1/kb-accelerator/{import_id}", headers=h)
        assert resp.status_code == 204

        # Verify deleted
        resp = await c.get(f"/api/v1/kb-accelerator/{import_id}", headers=h)
        assert resp.status_code == 404
