"""Integration tests for manage_ui tool."""

import asyncio
import base64

import pytest

from .test_helpers import DummyContext
import services.tools.manage_ui as manage_ui_mod


def run_async(coro):
    """Simple wrapper to run a coroutine synchronously."""
    loop = asyncio.new_event_loop()
    try:
        asyncio.set_event_loop(loop)
        return loop.run_until_complete(coro)
    finally:
        loop.close()
        asyncio.set_event_loop(None)


SAMPLE_UXML = """<ui:UXML xmlns:ui="UnityEngine.UIElements">
    <ui:Label text="Hello World" />
</ui:UXML>"""

SAMPLE_USS = """.label {
    font-size: 24px;
    color: white;
}"""


class TestManageUIPathValidation:
    """Tests for path validation logic."""

    def test_create_rejects_path_outside_assets(self, monkeypatch):
        captured = {}

        async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
            captured["params"] = params
            return {"success": True}

        monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)

        resp = run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="create",
            path="NotAssets/UI/Test.uxml",
            contents=SAMPLE_UXML,
        ))

        assert resp["success"] is False
        assert "Assets/" in resp["message"]

    def test_create_rejects_traversal(self, monkeypatch):
        async def fake_send(*_args, **_kwargs):
            return {"success": True}

        monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)

        resp = run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="create",
            path="Assets/../etc/passwd.uxml",
            contents=SAMPLE_UXML,
        ))

        assert resp["success"] is False
        # Path normalization resolves ".." so it either fails traversal or Assets/ check
        assert "traversal" in resp["message"] or "Assets/" in resp["message"]

    def test_create_rejects_invalid_extension(self, monkeypatch):
        async def fake_send(*_args, **_kwargs):
            return {"success": True}

        monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)

        resp = run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="create",
            path="Assets/UI/Test.cs",
            contents="some content",
        ))

        assert resp["success"] is False
        assert ".uxml or .uss" in resp["message"]

    def test_create_accepts_uxml_extension(self, monkeypatch):
        captured = {}

        async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
            captured["params"] = params
            return {"success": True, "message": "Created"}

        monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)

        resp = run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="create",
            path="Assets/UI/Menu.uxml",
            contents=SAMPLE_UXML,
        ))

        assert resp["success"] is True
        assert captured["params"]["path"] == "Assets/UI/Menu.uxml"

    def test_create_accepts_uss_extension(self, monkeypatch):
        captured = {}

        async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
            captured["params"] = params
            return {"success": True, "message": "Created"}

        monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)

        resp = run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="create",
            path="Assets/UI/Styles.uss",
            contents=SAMPLE_USS,
        ))

        assert resp["success"] is True
        assert captured["params"]["path"] == "Assets/UI/Styles.uss"


class TestManageUIContentsEncoding:
    """Tests for base64 content encoding."""

    def test_create_encodes_contents_as_base64(self, monkeypatch):
        captured = {}

        async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
            captured["params"] = params
            return {"success": True, "message": "Created"}

        monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)

        run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="create",
            path="Assets/UI/Test.uxml",
            contents=SAMPLE_UXML,
        ))

        params = captured["params"]
        assert params["contentsEncoded"] is True
        decoded = base64.b64decode(params["encodedContents"]).decode("utf-8")
        assert decoded == SAMPLE_UXML
        # Raw contents should NOT be in params (only encoded)
        assert "contents" not in params

    def test_update_encodes_contents_as_base64(self, monkeypatch):
        captured = {}

        async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
            captured["params"] = params
            return {"success": True, "message": "Updated"}

        monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)

        run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="update",
            path="Assets/UI/Test.uss",
            contents=SAMPLE_USS,
        ))

        params = captured["params"]
        assert params["contentsEncoded"] is True
        decoded = base64.b64decode(params["encodedContents"]).decode("utf-8")
        assert decoded == SAMPLE_USS


class TestManageUIActionRouting:
    """Tests for action-based parameter routing."""

    def test_read_uses_non_mutation_path(self, monkeypatch):
        captured = {}

        async def fake_send(_func, _instance, cmd, params, **kwargs):
            captured["cmd"] = cmd
            captured["params"] = params
            return {"success": True, "data": {"contents": "test"}}

        monkeypatch.setattr(manage_ui_mod, "send_with_unity_instance", fake_send)

        run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="read",
            path="Assets/UI/Test.uxml",
        ))

        assert captured["cmd"] == "manage_ui"
        assert captured["params"]["action"] == "read"

    def test_create_uses_mutation_path(self, monkeypatch):
        captured = {}

        async def fake_send(_ctx, _instance, cmd, _params, **kwargs):
            captured["cmd"] = cmd
            return {"success": True, "message": "Created"}

        monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)

        run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="create",
            path="Assets/UI/Test.uxml",
            contents=SAMPLE_UXML,
        ))

        assert captured["cmd"] == "manage_ui"

    def test_attach_ui_document_params(self, monkeypatch):
        captured = {}

        async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
            captured["params"] = params
            return {"success": True, "message": "Attached"}

        monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)

        run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="attach_ui_document",
            target="MyCanvas",
            source_asset="Assets/UI/Menu.uxml",
            panel_settings="Assets/UI/PanelSettings.asset",
            sort_order=5,
        ))

        params = captured["params"]
        assert params["action"] == "attach_ui_document"
        assert params["target"] == "MyCanvas"
        assert params["sourceAsset"] == "Assets/UI/Menu.uxml"
        assert params["panelSettings"] == "Assets/UI/PanelSettings.asset"
        assert params["sortOrder"] == 5

    def test_create_panel_settings_params(self, monkeypatch):
        captured = {}

        async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
            captured["params"] = params
            return {"success": True, "message": "Created"}

        monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)

        run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="create_panel_settings",
            path="Assets/UI/MyPanel.asset",
            scale_mode="ScaleWithScreenSize",
            reference_resolution={"width": 1920, "height": 1080},
        ))

        params = captured["params"]
        assert params["action"] == "create_panel_settings"
        assert params["scaleMode"] == "ScaleWithScreenSize"
        assert params["referenceResolution"] == {"width": 1920, "height": 1080}

    def test_get_visual_tree_params(self, monkeypatch):
        captured = {}

        async def fake_send(_func, _instance, _cmd, params, **kwargs):
            captured["params"] = params
            return {"success": True, "data": {"tree": {}}}

        monkeypatch.setattr(manage_ui_mod, "send_with_unity_instance", fake_send)

        run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="get_visual_tree",
            target="UIRoot",
            max_depth=5,
        ))

        params = captured["params"]
        assert params["action"] == "get_visual_tree"
        assert params["target"] == "UIRoot"
        assert params["maxDepth"] == 5

    def test_ping_uses_non_mutation_path(self, monkeypatch):
        captured = {}

        async def fake_send(_func, _instance, _cmd, params, **kwargs):
            captured["params"] = params
            return {"success": True, "message": "pong"}

        monkeypatch.setattr(manage_ui_mod, "send_with_unity_instance", fake_send)

        resp = run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="ping",
        ))

        assert resp["success"] is True
        assert captured["params"]["action"] == "ping"


class TestManageUINoneRemoval:
    """Tests that None values are properly excluded from params."""

    def test_none_params_excluded(self, monkeypatch):
        captured = {}

        async def fake_send(_func, _instance, _cmd, params, **kwargs):
            captured["params"] = params
            return {"success": True, "data": {}}

        monkeypatch.setattr(manage_ui_mod, "send_with_unity_instance", fake_send)

        run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="read",
            path="Assets/UI/Test.uxml",
            # All other params are None
        ))

        params = captured["params"]
        assert "target" not in params
        assert "sourceAsset" not in params
        assert "panelSettings" not in params
        assert "sortOrder" not in params
        assert "scaleMode" not in params
        assert "maxDepth" not in params


class TestManageUIReadResponse:
    """Tests for read response handling."""

    def test_read_decodes_base64_response(self, monkeypatch):
        encoded = base64.b64encode(SAMPLE_UXML.encode("utf-8")).decode("utf-8")

        async def fake_send(*_args, **_kwargs):
            return {
                "success": True,
                "data": {
                    "path": "Assets/UI/Test.uxml",
                    "contents": SAMPLE_UXML,
                    "encodedContents": encoded,
                    "contentsEncoded": True,
                }
            }

        monkeypatch.setattr(manage_ui_mod, "send_with_unity_instance", fake_send)

        resp = run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(),
            action="read",
            path="Assets/UI/Test.uxml",
        ))

        assert resp["success"] is True
        data = resp["data"]
        assert data["contents"] == SAMPLE_UXML
        assert "encodedContents" not in data
        assert "contentsEncoded" not in data


class TestManageUIRenderUI:
    """Tests for render_ui action."""

    def test_render_ui_routes_params(self, monkeypatch):
        captured = {}

        async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
            captured["params"] = params
            return {"success": True, "message": "Rendered",
                    "data": {"path": "Assets/Screenshots/test.png"}}

        monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)

        resp = run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(), action="render_ui",
            target="UIRoot", width=1280, height=720,
            include_image=True, max_resolution=480,
            screenshot_file_name="my-preview",
        ))
        assert resp["success"] is True
        p = captured["params"]
        assert p["action"] == "render_ui"
        assert p["target"] == "UIRoot"
        assert p["width"] == 1280
        assert p["height"] == 720
        assert p["include_image"] is True
        assert p["max_resolution"] == 480
        assert p["file_name"] == "my-preview"

    def test_render_ui_none_excluded(self, monkeypatch):
        captured = {}

        async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
            captured["params"] = params
            return {"success": True, "message": "ok"}

        monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)
        run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(), action="render_ui", target="X"))
        p = captured["params"]
        for k in ("width", "height", "include_image", "max_resolution", "file_name"):
            assert k not in p


class TestManageUILinkStylesheet:
    """Tests for link_stylesheet action."""

    def test_link_stylesheet_routes_params(self, monkeypatch):
        captured = {}

        async def fake_send(_ctx, _instance, _cmd, params, **kwargs):
            captured["params"] = params
            return {"success": True, "message": "Linked"}

        monkeypatch.setattr(manage_ui_mod, "send_mutation", fake_send)

        resp = run_async(manage_ui_mod.manage_ui(
            ctx=DummyContext(), action="link_stylesheet",
            path="Assets/UI/Menu.uxml",
            stylesheet="Assets/UI/Styles.uss",
        ))
        assert resp["success"] is True
        p = captured["params"]
        assert p["action"] == "link_stylesheet"
        assert p["path"] == "Assets/UI/Menu.uxml"
        assert p["stylesheet"] == "Assets/UI/Styles.uss"
        for k in ("width", "height", "include_image"):
            assert k not in p
