"""Devices mixin for Android ADB MCP Server.

Provides tools and resources for device discovery and management.
"""

import contextlib
import re
from typing import Any

from fastmcp import Context
from fastmcp.contrib.mcp_mixin import mcp_resource, mcp_tool

from ..config import is_developer_mode
from ..models import DeviceInfo
from .base import ADBBaseMixin


class DevicesMixin(ADBBaseMixin):
    """Mixin for Android device management.

    Provides tools for:
    - Listing connected devices
    - Getting device details
    - Setting current working device
    """

    def __init__(self) -> None:
        super().__init__()
        self._devices_cache: dict[str, DeviceInfo] = {}

    async def _refresh_devices(self) -> list[DeviceInfo]:
        """Refresh the internal devices cache.

        Returns:
            List of discovered devices
        """
        result = await self.run_adb(["devices", "-l"])

        if not result.success:
            return []

        devices = []
        lines = result.stdout.split("\n")[1:]  # Skip header

        for line in lines:
            if not line.strip():
                continue

            parts = line.split()
            if len(parts) >= 2:
                device_id = parts[0]
                status = parts[1]

                # Parse extended info (model:xxx product:xxx)
                model = None
                product = None
                for part in parts[2:]:
                    if part.startswith("model:"):
                        model = part.split(":", 1)[1]
                    elif part.startswith("product:"):
                        product = part.split(":", 1)[1]

                device = DeviceInfo(
                    device_id=device_id,
                    status=status,
                    model=model,
                    product=product,
                )
                devices.append(device)
                self._devices_cache[device_id] = device

        return devices

    @mcp_tool()
    async def devices_list(self) -> list[DeviceInfo]:
        """List all connected Android devices.

        Discovers devices connected via USB or network (adb connect).
        Use this first to identify available devices before other operations.

        Returns:
            List of connected devices with IDs, status, and model info
        """
        return await self._refresh_devices()

    @mcp_tool()
    async def devices_use(self, device_id: str) -> dict[str, Any]:
        """Set the current working device.

        All subsequent commands will target this device by default.
        Useful when multiple devices are connected.

        Args:
            device_id: Device serial number from devices_list

        Returns:
            Confirmation with device details
        """
        # Verify device exists
        devices = await self._refresh_devices()
        device = next((d for d in devices if d.device_id == device_id), None)

        if not device:
            return {
                "success": False,
                "error": f"Device {device_id} not found",
                "available": [d.device_id for d in devices],
            }

        if device.status != "device":
            return {
                "success": False,
                "error": f"Device {device_id} is {device.status}, not ready",
            }

        self.set_current_device(device_id)

        return {
            "success": True,
            "message": f"Now using device {device_id}",
            "device": device.model_dump(),
        }

    @mcp_tool()
    async def devices_current(self) -> dict[str, Any]:
        """Get information about the current working device.

        Returns:
            Current device info or error if none set
        """
        current = self.get_current_device()

        if not current:
            devices = await self._refresh_devices()
            if len(devices) == 1:
                # Auto-select if only one device
                return {
                    "device": None,
                    "message": "No device set, but only one available",
                    "available": devices[0].model_dump(),
                }
            return {
                "device": None,
                "error": "No current device set. Use devices_use() first.",
                "available": [d.device_id for d in devices],
            }

        device = self._devices_cache.get(current)
        if device:
            return {"device": device.model_dump()}

        return {"device": current, "cached_info": None}

    @mcp_resource(uri="adb://devices")
    async def resource_devices_list(self) -> dict[str, Any]:
        """Resource: List all connected Android devices.

        Lightweight enumeration for quick reference.
        """
        devices = await self._refresh_devices()
        return {
            "devices": [d.model_dump() for d in devices],
            "count": len(devices),
            "current": self.get_current_device(),
        }

    @mcp_resource(uri="adb://device/{device_id}")
    async def resource_device_info(self, device_id: str) -> dict[str, Any]:
        """Resource: Get detailed information about a specific device.

        Args:
            device_id: Device serial number

        Returns:
            Device details including Android version, model, etc.
        """
        # Ensure devices are loaded
        await self._refresh_devices()

        device = self._devices_cache.get(device_id)
        if not device:
            return {"error": f"Device {device_id} not found"}

        # Fetch additional properties
        props = {}
        for prop_name, prop_key in [
            ("android_version", "ro.build.version.release"),
            ("sdk_version", "ro.build.version.sdk"),
            ("manufacturer", "ro.product.manufacturer"),
            ("brand", "ro.product.brand"),
            ("device", "ro.product.device"),
        ]:
            value = await self.get_device_property(prop_key, device_id)
            if value:
                props[prop_name] = value

        return {
            **device.model_dump(),
            **props,
        }

    @mcp_tool()
    async def device_info(
        self,
        device_id: str | None = None,
    ) -> dict[str, Any]:
        """Get comprehensive device information.

        Returns device state including battery, wifi, storage, and system info.
        Useful for quick device health checks.

        Args:
            device_id: Target device

        Returns:
            Device information including battery, wifi, storage, etc.
        """
        info: dict[str, Any] = {}

        # Battery info — also serves as connectivity check
        battery = await self.run_shell_args(["dumpsys", "battery"], device_id)
        if not battery.success:
            return {
                "success": False,
                "error": battery.stderr or "No device connected",
            }

        info["success"] = True

        battery_info: dict[str, Any] = {}
        for line in battery.stdout.split("\n"):
            if "level:" in line:
                with contextlib.suppress(ValueError):
                    battery_info["level"] = int(line.split(":")[1].strip())
            elif "status:" in line:
                status_map = {
                    "1": "unknown",
                    "2": "charging",
                    "3": "discharging",
                    "4": "not_charging",
                    "5": "full",
                }
                status = line.split(":")[1].strip()
                battery_info["status"] = status_map.get(status, status)
            elif "plugged:" in line:
                plugged_map = {
                    "0": "unplugged",
                    "1": "AC",
                    "2": "USB",
                    "4": "wireless",
                }
                plugged = line.split(":")[1].strip()
                battery_info["plugged"] = plugged_map.get(plugged, plugged)
        info["battery"] = battery_info

        # Get IP address — parse ip addr output in Python (no pipes)
        ip_result = await self.run_shell_args(
            ["ip", "addr", "show", "wlan0"], device_id
        )
        if ip_result.success:
            inet_match = re.search(r"inet (\d+\.\d+\.\d+\.\d+)/", ip_result.stdout)
            if inet_match:
                info["ip_address"] = inet_match.group(1)

        # WiFi connection info — parse dumpsys in Python (no pipes)
        wifi = await self.run_shell_args(["dumpsys", "wifi"], device_id)
        if wifi.success:
            for wifi_line in wifi.stdout.split("\n"):
                if "mWifiInfo" in wifi_line and "SSID:" in wifi_line:
                    try:
                        ssid_part = wifi_line.split("SSID:")[1].split(",")[0].strip()
                        info["wifi_ssid"] = ssid_part.strip('"')
                    except IndexError:
                        pass
                    break

        # System properties
        props_to_fetch = [
            ("android_version", "ro.build.version.release"),
            ("sdk_version", "ro.build.version.sdk"),
            ("model", "ro.product.model"),
            ("manufacturer", "ro.product.manufacturer"),
            ("device_name", "ro.product.device"),
        ]
        for key, prop in props_to_fetch:
            value = await self.get_device_property(prop, device_id)
            if value:
                info[key] = value

        # Storage info — parse df output in Python (no pipes)
        storage = await self.run_shell_args(["df", "/data"], device_id)
        if storage.success:
            lines = storage.stdout.strip().split("\n")
            if len(lines) >= 2:
                parts = lines[-1].split()
                if len(parts) >= 4:
                    with contextlib.suppress(ValueError):
                        info["storage"] = {
                            "total_kb": int(parts[1]),
                            "used_kb": int(parts[2]),
                            "available_kb": int(parts[3]),
                        }

        return info

    @mcp_tool(
        tags={"developer"},
        annotations={"requires": "developer_mode"},
    )
    async def device_reboot(
        self,
        ctx: Context,
        mode: str | None = None,
        device_id: str | None = None,
    ) -> dict[str, Any]:
        """Reboot the device.

        [DEVELOPER MODE] Reboots the Android device.
        Requires user confirmation before proceeding.

        Args:
            ctx: MCP context for elicitation/logging
            mode: Optional reboot mode:
                  - None: Normal reboot
                  - "recovery": Boot to recovery mode
                  - "bootloader": Boot to bootloader/fastboot
            device_id: Target device

        Returns:
            Reboot command result
        """
        if not is_developer_mode():
            return {
                "success": False,
                "error": "Developer mode required",
            }

        # Elicit confirmation for this dangerous action
        mode_desc = mode or "normal"
        await ctx.warning(f"Reboot requested: {mode_desc} mode")

        confirmation = await ctx.elicit(
            f"Are you sure you want to reboot the device in "
            f"{mode_desc} mode? "
            "This will interrupt any running operations.",
            ["Yes, reboot now", "Cancel"],
        )

        if confirmation.action != "accept" or confirmation.content == "Cancel":
            await ctx.info("Reboot cancelled by user")
            return {
                "success": False,
                "cancelled": True,
                "message": "Reboot cancelled by user",
            }

        await ctx.info(f"Initiating {mode_desc} reboot...")

        cmd = ["reboot"]
        if mode:
            cmd.append(mode)

        result = await self.run_adb(cmd, device_id)

        if result.success:
            await ctx.info("Reboot command sent successfully")
        else:
            await ctx.error(f"Reboot failed: {result.stderr}")

        return {
            "success": result.success,
            "action": "reboot",
            "mode": mode_desc,
            "error": result.stderr if not result.success else None,
        }

    @mcp_tool(
        tags={"developer"},
        annotations={"requires": "developer_mode"},
    )
    async def logcat_capture(
        self,
        lines: int = 100,
        filter_spec: str | None = None,
        clear_first: bool = False,
        device_id: str | None = None,
    ) -> dict[str, Any]:
        """Capture logcat output.

        [DEVELOPER MODE] Retrieves Android system logs.
        Essential for debugging app crashes and system issues.

        Args:
            lines: Number of recent log lines to capture (default 100)
            filter_spec: Filter by tag:priority (e.g., "MyApp:D *:S")
            clear_first: Clear the log buffer before capturing
            device_id: Target device

        Returns:
            Logcat output
        """
        if not is_developer_mode():
            return {
                "success": False,
                "error": "Developer mode required",
            }

        # Clear first if requested
        if clear_first:
            await self.run_shell_args(["logcat", "-c"], device_id)

        # Build command as args list — filter_spec is split safely
        cmd = ["logcat", "-d", "-t", str(lines)]
        if filter_spec:
            # Split filter spec on whitespace (e.g., "MyApp:D *:S")
            # Each token is a separate arg, safely quoted by run_shell_args
            cmd.extend(filter_spec.split())

        result = await self.run_shell_args(cmd, device_id)

        return {
            "success": result.success,
            "lines_requested": lines,
            "filter": filter_spec,
            "output": result.stdout,
            "error": result.stderr if not result.success else None,
        }

    @mcp_tool(
        tags={"developer"},
        annotations={"requires": "developer_mode"},
    )
    async def logcat_clear(
        self,
        device_id: str | None = None,
    ) -> dict[str, Any]:
        """Clear the logcat buffer.

        [DEVELOPER MODE] Clears all logs from the device log buffer.
        Useful before reproducing an issue to get clean logs.

        Args:
            device_id: Target device

        Returns:
            Success status
        """
        if not is_developer_mode():
            return {
                "success": False,
                "error": "Developer mode required",
            }

        result = await self.run_shell_args(["logcat", "-c"], device_id)

        return {
            "success": result.success,
            "action": "logcat_clear",
            "error": result.stderr if not result.success else None,
        }
