from __future__ import annotations

from dataclasses import dataclass
from typing import Any

import pytest

from nomo_e2e.adapters.webdriver import WebDriverAdapter
from nomo_e2e.exceptions import WebViewElementNotFoundError


@dataclass
class FakeResponse:
    ok: bool
    status: int
    payload: dict[str, Any]

    def json(self) -> dict[str, Any]:
        return self.payload

    def text(self) -> str:
        return str(self.payload)


class FakeRequestContext:
    def __init__(self) -> None:
        self.calls: list[tuple[str, str, dict[str, Any]]] = []
        self._responses: dict[tuple[str, str], list[FakeResponse]] = {}
        self.disposed = False

    def enqueue(self, method: str, url: str, response: FakeResponse) -> None:
        self._responses.setdefault((method, url), []).append(response)

    def _pop(self, method: str, url: str, data: dict[str, Any] | None) -> FakeResponse:
        self.calls.append((method, url, data or {}))
        queue = self._responses.get((method, url))
        if not queue:
            raise RuntimeError(f"No fake response for {method} {url}")
        return queue.pop(0)

    def post(self, url: str, **kwargs: Any) -> FakeResponse:
        return self._pop("POST", url, kwargs.get("data"))

    def get(self, url: str, **kwargs: Any) -> FakeResponse:
        return self._pop("GET", url, kwargs.get("data"))

    def delete(self, url: str, **kwargs: Any) -> FakeResponse:
        return self._pop("DELETE", url, kwargs.get("data"))

    def dispose(self) -> None:
        self.disposed = True


def test_connect_creates_session() -> None:
    endpoint = "http://127.0.0.1:4445"
    fake_request = FakeRequestContext()
    fake_request.enqueue(
        "POST",
        f"{endpoint}/session",
        FakeResponse(ok=True, status=200, payload={"value": {"sessionId": "s-1"}}),
    )
    adapter = WebDriverAdapter(endpoint=endpoint, request_context=fake_request)
    adapter.connect()
    assert adapter.is_connected


def test_click_raises_not_found_when_selector_missing() -> None:
    endpoint = "http://127.0.0.1:4445"
    fake_request = FakeRequestContext()
    fake_request.enqueue(
        "POST",
        f"{endpoint}/session",
        FakeResponse(ok=True, status=200, payload={"value": {"sessionId": "s-1"}}),
    )
    elements_url = f"{endpoint}/session/s-1/elements"
    for _ in range(3):
        fake_request.enqueue(
            "POST",
            elements_url,
            FakeResponse(ok=True, status=200, payload={"value": []}),
        )
    adapter = WebDriverAdapter(endpoint=endpoint, request_context=fake_request)
    adapter.connect()
    with pytest.raises(WebViewElementNotFoundError):
        adapter.click("#missing", timeout_ms=500)


def test_type_text_posts_value_payload() -> None:
    endpoint = "http://127.0.0.1:4445"
    fake_request = FakeRequestContext()
    fake_request.enqueue(
        "POST",
        f"{endpoint}/session",
        FakeResponse(ok=True, status=200, payload={"value": {"sessionId": "s-1"}}),
    )
    elements_url = f"{endpoint}/session/s-1/elements"
    fake_request.enqueue(
        "POST",
        elements_url,
        FakeResponse(
            ok=True,
            status=200,
            payload={"value": [{"element-6066-11e4-a52e-4f735466cecf": "e-1"}]},
        ),
    )
    fake_request.enqueue(
        "POST",
        elements_url,
        FakeResponse(
            ok=True,
            status=200,
            payload={"value": [{"element-6066-11e4-a52e-4f735466cecf": "e-1"}]},
        ),
    )
    fake_request.enqueue(
        "POST",
        f"{endpoint}/session/s-1/element/e-1/clear",
        FakeResponse(ok=True, status=200, payload={"value": None}),
    )
    fake_request.enqueue(
        "POST",
        f"{endpoint}/session/s-1/element/e-1/value",
        FakeResponse(ok=True, status=200, payload={"value": None}),
    )
    adapter = WebDriverAdapter(endpoint=endpoint, request_context=fake_request)
    adapter.connect()
    adapter.type_text("#name", "boss", timeout_ms=1000)
    last_call = fake_request.calls[-1]
    assert last_call[0] == "POST"
    assert last_call[1].endswith("/value")
    assert last_call[2]["text"] == "boss"
