"""Tests for TaskEngine — submit, cancel, VLM config, task eviction."""

from __future__ import annotations

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

import pytest

from device_agent.ai.task_engine import TaskEngine, TaskStatus
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="Stub",
            os="Android",
            width=1080,
            height=2400,
            capabilities=[DeviceCapability.TOUCH],
            metadata={"serial": "STUB123"},
        )

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

    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 ""

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


def _make_engine(**kwargs) -> TaskEngine:
    return TaskEngine(runtime=StubRuntime(), **kwargs)


@pytest.mark.asyncio
async def test_submit_creates_task():
    engine = _make_engine()
    await engine.start()
    task = await engine.submit("tap on settings")
    assert task.id
    assert task.status == TaskStatus.QUEUED
    assert task.instruction == "tap on settings"
    await engine.stop()


@pytest.mark.asyncio
async def test_get_task_returns_none_for_unknown():
    engine = _make_engine()
    assert engine.get_task("nonexistent") is None


@pytest.mark.asyncio
async def test_list_tasks_ordered_by_created():
    engine = _make_engine()
    await engine.start()
    t1 = await engine.submit("first")
    t2 = await engine.submit("second")
    tasks = engine.list_tasks()
    assert len(tasks) >= 2
    assert tasks[0].created_at >= tasks[1].created_at
    await engine.stop()


@pytest.mark.asyncio
async def test_cancel_unknown_task():
    engine = _make_engine()
    result = await engine.cancel("nonexistent")
    assert result is False


@pytest.mark.asyncio
async def test_vlm_config_passed():
    engine = _make_engine(
        vlm_api_key="test-key",
        vlm_base_url="http://localhost:8000",
        vlm_model="gpt-4o-mini",
        vlm_provider="openai",
    )
    assert engine._vlm_api_key == "test-key"
    assert engine._vlm_base_url == "http://localhost:8000"
    assert engine._vlm_model == "gpt-4o-mini"
    assert engine._vlm_provider == "openai"


@pytest.mark.asyncio
async def test_evict_old_tasks():
    engine = _make_engine()
    engine.MAX_COMPLETED_TASKS = 2

    import time
    for i in range(5):
        from device_agent.ai.task_engine import Task
        t = Task(id=f"t{i}", instruction=f"task {i}", status=TaskStatus.COMPLETED)
        t.completed_at = time.time() - (5 - i)
        engine._tasks[t.id] = t

    assert len(engine._tasks) == 5
    engine._evict_old_tasks()
    assert len(engine._tasks) == 2
    assert "t3" in engine._tasks
    assert "t4" in engine._tasks


@pytest.mark.asyncio
async def test_on_step_callback():
    step_calls = []

    async def on_step(task_id: str, step: dict):
        step_calls.append((task_id, step))

    engine = _make_engine(on_step=on_step)
    assert engine._on_step is not None
