"""Tests for DeviceAgentAPI — routes, CORS, path traversal guard, error handling."""

from __future__ import annotations

import json
from typing import AsyncIterator
from unittest.mock import AsyncMock

import pytest
from aiohttp import web
from aiohttp.test_utils import AioHTTPTestCase, TestClient, TestServer

from device_agent.ai.task_engine import TaskEngine
from device_agent.api import DeviceAgentAPI
from device_agent.runtime.base import (
    DeviceCapability,
    DeviceInfo,
    DeviceRuntime,
    DeviceType,
)


class StubRuntime(DeviceRuntime):
    async def connect(self) -> None:
        pass

    async def disconnect(self) -> None:
        pass

    async def get_info(self) -> DeviceInfo:
        return DeviceInfo(
            type=DeviceType.ANDROID,
            name="TestDevice",
            os="Android",
            width=1080,
            height=2400,
            capabilities=[DeviceCapability.TOUCH, DeviceCapability.SCREENSHOT],
            metadata={"serial": "T1", "connected": True},
        )

    async def screenshot(self) -> bytes:
        return b"\x89PNG\r\n\x1a\n"

    async def get_ui_tree(self) -> list[dict]:
        return []

    async def tap(self, x: int, y: int) -> None:
        pass

    async def long_press(self, x: int, y: int, duration_ms: int = 1000) -> None:
        pass

    async def swipe(self, x1: int, y1: int, x2: int, y2: int, duration_ms: int = 300) -> None:
        pass

    async def type_text(self, text: str) -> None:
        pass

    async def press_key(self, keycode: str) -> None:
        pass

    async def open_app(self, package_or_path: str) -> None:
        pass

    async def install_app(self, apk_path: str) -> None:
        pass

    async def execute_shell(self, command: str) -> str:
        return "ok"

    async def start_screen_stream(self) -> AsyncIterator[bytes]:
        yield b""


@pytest.fixture
def app():
    runtime = StubRuntime()
    engine = TaskEngine(runtime=runtime)
    api = DeviceAgentAPI(runtime=runtime, task_engine=engine, port=0)
    return api._app


@pytest.fixture
async def client(app, aiohttp_client):
    return await aiohttp_client(app)


@pytest.mark.asyncio
async def test_health(client):
    resp = await client.get("/health")
    assert resp.status == 200
    body = await resp.json()
    assert body["status"] == "ok"
    assert body["device"] == "TestDevice"


@pytest.mark.asyncio
async def test_device_info(client):
    resp = await client.get("/device/info")
    assert resp.status == 200
    body = await resp.json()
    assert body["name"] == "TestDevice"
    assert body["type"] == "android"


@pytest.mark.asyncio
async def test_screenshot_json(client):
    resp = await client.get("/device/screenshot")
    assert resp.status == 200
    body = await resp.json()
    assert "image" in body
    assert body["format"] == "png"


@pytest.mark.asyncio
async def test_screenshot_png(client):
    resp = await client.get("/device/screenshot.png")
    assert resp.status == 200
    assert resp.content_type == "image/png"


@pytest.mark.asyncio
async def test_device_action_tap(client):
    resp = await client.post(
        "/device/action",
        json={"action": "tap", "parameters": {"x": 100, "y": 200}},
    )
    assert resp.status == 200
    body = await resp.json()
    assert body["ok"] is True


@pytest.mark.asyncio
async def test_device_action_unknown(client):
    resp = await client.post(
        "/device/action",
        json={"action": "dance", "parameters": {}},
    )
    assert resp.status == 400


@pytest.mark.asyncio
async def test_cors_preflight(client):
    resp = await client.options("/health")
    assert resp.status == 204
    assert "Access-Control-Allow-Origin" in resp.headers


@pytest.mark.asyncio
async def test_cors_on_normal_response(client):
    resp = await client.get("/health")
    assert "Access-Control-Allow-Origin" in resp.headers


@pytest.mark.asyncio
async def test_invalid_json_body(client):
    resp = await client.post(
        "/device/action",
        data=b"not json",
        headers={"Content-Type": "application/json"},
    )
    assert resp.status == 400
    body = await resp.json()
    assert "error" in body


@pytest.mark.asyncio
async def test_create_task(client):
    resp = await client.post("/tasks", json={"instruction": "open settings"})
    assert resp.status == 201
    body = await resp.json()
    assert "task_id" in body
    assert body["status"] == "queued"


@pytest.mark.asyncio
async def test_create_task_missing_instruction(client):
    resp = await client.post("/tasks", json={})
    assert resp.status == 400


@pytest.mark.asyncio
async def test_list_tasks(client):
    resp = await client.get("/tasks")
    assert resp.status == 200
    body = await resp.json()
    assert "tasks" in body


@pytest.mark.asyncio
async def test_get_task_not_found(client):
    resp = await client.get("/tasks/nonexistent")
    assert resp.status == 404


@pytest.mark.asyncio
async def test_cancel_task_not_found(client):
    resp = await client.post("/tasks/nonexistent/cancel")
    assert resp.status == 404
