"""MCP Resources — live-state snapshots the LLM can poll without tool calls.

Resources use a module-level reference to SessionState, set during lifespan.
This is necessary because FastMCP resource functions don't receive Context
injection — their parameters map to URI template variables.
"""

import contextlib
import logging

from mcjtag.server import mcp
from mcjtag.state import SessionState

log = logging.getLogger(__name__)

# Module-level reference, set by lifespan via set_state()
_state: SessionState | None = None


def set_state(state: SessionState) -> None:
    """Called from lifespan to make session state available to resources."""
    global _state
    _state = state


def _no_session_msg() -> str:
    return "Not connected to OpenOCD. Use connect or start_openocd first."


@mcp.resource("jtag://target/state")
async def resource_target_state() -> dict:
    """Current target execution state and program counter."""
    if _state is None or not _state.session:
        return {"error": _no_session_msg()}

    try:
        ts = await _state.session.target.state()
        return {
            "name": ts.name,
            "state": ts.state,
            "pc": f"0x{ts.current_pc:08x}" if ts.current_pc is not None else None,
            "halted": ts.state == "halted",
        }
    except Exception as exc:
        return {"error": str(exc)}


@mcp.resource("jtag://registers/all")
async def resource_registers() -> dict:
    """All CPU register values (target must be halted)."""
    if _state is None or not _state.session:
        return {"error": _no_session_msg()}

    try:
        all_regs = await _state.session.registers.read_all()
        return {
            "registers": {name: f"0x{r.value:0{r.size // 4}x}" for name, r in all_regs.items()},
            "count": len(all_regs),
        }
    except Exception as exc:
        return {"error": str(exc)}


@mcp.resource("jtag://flash/banks")
async def resource_flash_banks() -> dict:
    """Flash bank topology."""
    if _state is None or not _state.session:
        return {"error": _no_session_msg()}

    try:
        banks = await _state.session.flash.banks()
        return {
            "banks": [
                {
                    "index": b.index,
                    "name": b.name,
                    "base": f"0x{b.base:08x}",
                    "size": b.size,
                    "target": b.target,
                }
                for b in banks
            ],
            "count": len(banks),
        }
    except Exception as exc:
        return {"error": str(exc)}


@mcp.resource("jtag://jtag/chain")
async def resource_jtag_chain() -> dict:
    """JTAG scan chain — TAPs and IDCODEs."""
    if _state is None or not _state.session:
        return {"error": _no_session_msg()}

    try:
        taps = await _state.session.jtag.scan_chain()
        return {
            "taps": [
                {
                    "name": t.name,
                    "idcode": f"0x{t.idcode:08x}",
                    "ir_length": t.ir_length,
                    "enabled": t.enabled,
                }
                for t in taps
            ],
            "count": len(taps),
        }
    except Exception as exc:
        return {"error": str(exc)}


@mcp.resource("jtag://svd/peripherals")
async def resource_svd_peripherals() -> dict:
    """Loaded SVD peripheral list."""
    if _state is None or not _state.session:
        return {"error": _no_session_msg()}

    svd = _state.session.svd
    if not svd.loaded:
        return {"svd_loaded": False, "peripherals": []}

    return {
        "svd_loaded": True,
        "svd_path": _state.svd_path,
        "peripherals": svd.list_peripherals(),
    }


@mcp.resource("jtag://svd/{peripheral}")
async def resource_svd_peripheral(peripheral: str) -> dict:
    """Decoded register values for a specific SVD peripheral."""
    if _state is None or not _state.session:
        return {"error": _no_session_msg()}

    svd = _state.session.svd
    if not svd.loaded:
        return {"error": "No SVD file loaded"}

    try:
        decoded = await svd.read_peripheral(peripheral)
        registers = {}
        for reg_name, dr in decoded.items():
            registers[reg_name] = {
                "address": f"0x{dr.address:08x}",
                "raw_value": f"0x{dr.raw_value:08x}",
                "fields": [
                    {
                        "name": f.name,
                        "value": f.value,
                        "width": f.width,
                        "offset": f.offset,
                        "description": f.description,
                    }
                    for f in dr.fields
                ],
            }
        return {"peripheral": peripheral, "registers": registers}
    except Exception as exc:
        return {"error": str(exc)}


@mcp.resource("jtag://transport/info")
async def resource_transport_info() -> dict:
    """Active transport (JTAG/SWD) and adapter info."""
    if _state is None or not _state.session:
        return {"error": _no_session_msg()}

    try:
        transport = _state.session.transport
        current = await transport.select()
        available = await transport.list()
        adapter = await transport.adapter_info()

        speed = None
        with contextlib.suppress(Exception):
            speed = await transport.adapter_speed()

        return {
            "transport": current,
            "adapter": adapter,
            "speed_khz": speed,
            "available_transports": available,
        }
    except Exception as exc:
        return {"error": str(exc)}


@mcp.resource("jtag://breakpoints")
async def resource_breakpoints() -> dict:
    """Active breakpoints and watchpoints."""
    if _state is None or not _state.session:
        return {"error": _no_session_msg()}

    try:
        bps = await _state.session.breakpoints.list()
        wps = await _state.session.breakpoints.list_watchpoints()
        return {
            "breakpoints": [
                {
                    "address": f"0x{bp.address:08x}",
                    "type": bp.type,
                    "length": bp.length,
                    "enabled": bp.enabled,
                }
                for bp in bps
            ],
            "watchpoints": [
                {
                    "address": f"0x{wp.address:08x}",
                    "length": wp.length,
                    "access": wp.access,
                }
                for wp in wps
            ],
        }
    except Exception as exc:
        return {"error": str(exc)}


@mcp.resource("jtag://swd/dap")
async def resource_dap_info() -> dict:
    """DAP identification and Access Port summary."""
    if _state is None or not _state.session:
        return {"error": _no_session_msg()}

    try:
        info = await _state.session.swd.info()
        aps = await _state.session.swd.list_aps()
        return {
            "name": info.name,
            "dpidr": f"0x{info.dpidr:08x}",
            "ap_count": info.ap_count,
            "aps": [
                {
                    "index": ap.index,
                    "idr": f"0x{ap.idr:08x}",
                    "base": f"0x{ap.base:08x}",
                    "type": ap.ap_type,
                }
                for ap in aps
            ],
        }
    except Exception as exc:
        return {"error": str(exc)}
