from __future__ import annotations

import ast
from pathlib import Path
from types import SimpleNamespace


def load_cursor_method(source: str):
    tree = ast.parse(source)
    target = None
    for node in ast.walk(tree):
        if isinstance(node, ast.FunctionDef) and node.name == "get_cursor_state":
            target = node
            break
    assert target is not None, "get_cursor_state missing"
    fn_node = ast.FunctionDef(
        name="get_cursor_state",
        args=target.args,
        body=target.body,
        decorator_list=[],
        returns=target.returns,
        type_comment=target.type_comment,
    )
    module = ast.fix_missing_locations(ast.Module(body=[fn_node], type_ignores=[]))

    class FakeWin32Api:
        cursor = (0, 0)

        @classmethod
        def GetCursorPos(cls):
            return cls.cursor

    namespace = {"win32api": FakeWin32Api}
    exec(compile(module, "<cursor_method>", "exec"), namespace)
    return namespace["get_cursor_state"], FakeWin32Api


def main() -> None:
    root = Path(__file__).resolve().parent
    capture = (root / "yjm_win2rtc_capture.py").read_text(encoding="utf-8")
    viewer = (root / "web" / "viewer.html").read_text(encoding="utf-8")

    assert 'BUILD_VERSION = "2.3.8.313"' in capture
    assert 'BUILD_TAG = "v313"' in capture
    assert '"inside_capture": bool(inside_capture)' in capture
    assert '"capture_rect": {"left": left, "top": top, "width": width, "height": height}' in capture
    assert '"frame_size": {"width": fw, "height": fh}' in capture

    get_cursor_state, fake_api = load_cursor_method(capture)
    self_obj = SimpleNamespace(
        capture_worker=SimpleNamespace(
            last_capture_rect={"left": 100, "top": 200, "width": 400, "height": 200},
            last_frame_size={"width": 800, "height": 400},
        )
    )

    fake_api.cursor = (300, 300)
    inside = get_cursor_state(self_obj)
    assert inside["ok"] is True
    assert inside["inside_capture"] is True
    assert inside["frame_x"] == 400.0 and inside["frame_y"] == 200.0, inside

    fake_api.cursor = (550, 300)
    outside = get_cursor_state(self_obj)
    assert outside["ok"] is True
    assert outside["inside_capture"] is False
    assert outside["frame_x"] == 900.0 and outside["frame_y"] == 200.0, outside

    assert "function applyCursorState(cur,focus)" in viewer
    assert "cur.inside_capture!==false" in viewer
    assert "if(focus&&!fit) centerViewOnRemotePoint(fx,fy)" in viewer
    assert 'id="pointerFindBtn"' not in viewer
    assert 'id="mobilePointerFindBtn"' not in viewer
    assert 'data-cmd="/cursor"' not in viewer
    print("OK test_rtc8790_cursor_state_v205")


if __name__ == "__main__":
    main()
