"""
Wokwi diagram.json parse/format utilities (Python port of frontend/src/utils/wokwiZip.ts).
Handles conversion between Wokwi diagram format and the Velxio internal circuit format.
"""

from __future__ import annotations

from typing import Any

# ---------------------------------------------------------------------------
# Type aliases / data models (plain dicts for simplicity)
# ---------------------------------------------------------------------------

# Wokwi diagram.json structure:
# {
#   "version": 1,
#   "author": "...",
#   "editor": "wokwi",
#   "parts": [ { "type": "...", "id": "...", "top": 0, "left": 0, "attrs": {} } ],
#   "connections": [ ["partId:pinName", "partId:pinName", "color", []] ]
# }

# ---------------------------------------------------------------------------
# Board type mappings
# ---------------------------------------------------------------------------

WOKWI_TO_VELXIO_BOARD: dict[str, str] = {
    "wokwi-arduino-uno": "arduino:avr:uno",
    "wokwi-arduino-mega": "arduino:avr:mega",
    "wokwi-arduino-nano": "arduino:avr:nano",
    "wokwi-pi-pico": "rp2040:rp2040:rpipico",
    "raspberry-pi-pico": "rp2040:rp2040:rpipico",
    "wokwi-esp32-devkit-v1": "esp32:esp32:esp32",
}

VELXIO_TO_WOKWI_BOARD: dict[str, str] = {v: k for k, v in WOKWI_TO_VELXIO_BOARD.items()}

# Default board FQBN when none can be detected
DEFAULT_BOARD_FQBN = "arduino:avr:uno"

# ---------------------------------------------------------------------------
# Color helpers
# ---------------------------------------------------------------------------

COLOR_NAMES: dict[str, str] = {
    "red": "#ff0000",
    "green": "#00ff00",
    "blue": "#0000ff",
    "yellow": "#ffff00",
    "orange": "#ffa500",
    "purple": "#800080",
    "white": "#ffffff",
    "black": "#000000",
    "cyan": "#00ffff",
    "magenta": "#ff00ff",
    "lime": "#00ff00",
    "pink": "#ffc0cb",
    "gray": "#808080",
    "grey": "#808080",
    "brown": "#a52a2a",
    "gold": "#ffd700",
    "silver": "#c0c0c0",
}

HEX_TO_NAME: dict[str, str] = {v: k for k, v in COLOR_NAMES.items()}


def color_to_hex(color: str) -> str:
    """Normalise a color string to lowercase hex (e.g. 'red' -> '#ff0000')."""
    c = color.strip().lower()
    if c in COLOR_NAMES:
        return COLOR_NAMES[c]
    if c.startswith("#"):
        return c
    return "#" + c


def hex_to_color_name(hex_color: str) -> str:
    """Return a friendly color name if one exists, otherwise return the hex string."""
    normalized = hex_color.strip().lower()
    return HEX_TO_NAME.get(normalized, hex_color)


# ---------------------------------------------------------------------------
# Wokwi → Velxio conversion
# ---------------------------------------------------------------------------

BOARD_PART_TYPES = set(WOKWI_TO_VELXIO_BOARD.keys())


def _detect_board_fqbn(parts: list[dict[str, Any]]) -> str:
    """Return the board FQBN inferred from the parts list."""
    for part in parts:
        part_type = part.get("type", "")
        if part_type in WOKWI_TO_VELXIO_BOARD:
            return WOKWI_TO_VELXIO_BOARD[part_type]
    return DEFAULT_BOARD_FQBN


def parse_wokwi_diagram(diagram: dict[str, Any]) -> dict[str, Any]:
    """
    Convert a Wokwi diagram.json payload into a Velxio circuit object.

    The returned object has the shape:
    {
        "board_fqbn": str,
        "components": [ { "id", "type", "left", "top", "rotate", "attrs" } ],
        "connections": [ { "from_part", "from_pin", "to_part", "to_pin", "color" } ],
        "version": int,
    }
    """
    parts: list[dict[str, Any]] = diagram.get("parts", [])
    raw_connections: list[Any] = diagram.get("connections", [])

    board_fqbn = _detect_board_fqbn(parts)

    # Build component list (skip board parts – they are implicit in board_fqbn)
    components: list[dict[str, Any]] = []
    for part in parts:
        components.append(
            {
                "id": part.get("id", ""),
                "type": part.get("type", ""),
                "left": float(part.get("left", 0)),
                "top": float(part.get("top", 0)),
                "rotate": int(part.get("rotate", 0)),
                "attrs": dict(part.get("attrs", {})),
            }
        )

    # Build connection list
    connections: list[dict[str, Any]] = []
    for conn in raw_connections:
        # Each connection is [from, to, color, segments]
        if not isinstance(conn, (list, tuple)) or len(conn) < 2:
            continue
        from_endpoint: str = conn[0]
        to_endpoint: str = conn[1]
        color_raw: str = conn[2] if len(conn) > 2 else "green"
        color = color_to_hex(color_raw)

        # "partId:pinName" → split at first ':'
        from_parts = from_endpoint.split(":", 1)
        to_parts = to_endpoint.split(":", 1)
        connections.append(
            {
                "from_part": from_parts[0],
                "from_pin": from_parts[1] if len(from_parts) > 1 else "",
                "to_part": to_parts[0],
                "to_pin": to_parts[1] if len(to_parts) > 1 else "",
                "color": color,
            }
        )

    return {
        "board_fqbn": board_fqbn,
        "components": components,
        "connections": connections,
        "version": int(diagram.get("version", 1)),
    }


# ---------------------------------------------------------------------------
# Velxio → Wokwi conversion
# ---------------------------------------------------------------------------


def format_wokwi_diagram(
    circuit: dict[str, Any],
    author: str = "velxio",
) -> dict[str, Any]:
    """
    Convert a Velxio circuit object back into a Wokwi diagram.json payload.
    """
    board_fqbn: str = circuit.get("board_fqbn", DEFAULT_BOARD_FQBN)
    wokwi_board_type: str = VELXIO_TO_WOKWI_BOARD.get(board_fqbn, "wokwi-arduino-uno")

    # Build parts list; inject board part first if not already present
    raw_components: list[dict[str, Any]] = circuit.get("components", [])

    board_present = any(c.get("type") == wokwi_board_type for c in raw_components)
    parts: list[dict[str, Any]] = []

    if not board_present:
        parts.append(
            {
                "type": wokwi_board_type,
                "id": "uno",
                "top": 0,
                "left": 0,
                "attrs": {},
            }
        )

    for comp in raw_components:
        parts.append(
            {
                "type": comp.get("type", ""),
                "id": comp.get("id", ""),
                "top": comp.get("top", 0),
                "left": comp.get("left", 0),
                "rotate": comp.get("rotate", 0),
                "attrs": comp.get("attrs", {}),
            }
        )

    # Build connections list  [ [from, to, color, []] ]
    raw_connections: list[dict[str, Any]] = circuit.get("connections", [])
    connections: list[list[Any]] = []
    for conn in raw_connections:
        from_endpoint = f"{conn.get('from_part', '')}:{conn.get('from_pin', '')}"
        to_endpoint = f"{conn.get('to_part', '')}:{conn.get('to_pin', '')}"
        color = hex_to_color_name(conn.get("color", "green"))
        connections.append([from_endpoint, to_endpoint, color, []])

    return {
        "version": int(circuit.get("version", 1)),
        "author": author,
        "editor": "velxio",
        "parts": parts,
        "connections": connections,
    }


# ---------------------------------------------------------------------------
# Code template generation
# ---------------------------------------------------------------------------

COMPONENT_PIN_TEMPLATES: dict[str, str] = {
    "wokwi-led": "pinMode({pin}, OUTPUT); // LED",
    "wokwi-pushbutton": "pinMode({pin}, INPUT_PULLUP); // Button",
    "wokwi-buzzer": "pinMode({pin}, OUTPUT); // Buzzer",
    "wokwi-servo": "// Servo on pin {pin}: use Servo library",
}


def generate_arduino_sketch(circuit: dict[str, Any], sketch_name: str = "sketch") -> str:
    """
    Generate a minimal Arduino sketch (.ino) from a Velxio circuit definition.

    Returns the .ino file content as a string.
    """
    components = circuit.get("components", [])
    connections = circuit.get("connections", [])

    # Determine unique pins referenced by connections that involve the board
    board_pin_map: dict[str, list[str]] = {}  # component_id -> [pin_names]
    board_part_ids = {
        c["id"] for c in components if c.get("type", "") in BOARD_PART_TYPES
    }

    for conn in connections:
        for side in ("from", "to"):
            part = conn.get(f"{side}_part", "")
            pin = conn.get(f"{side}_pin", "")
            if part in board_part_ids and pin:
                other_side = "to" if side == "from" else "from"
                other_part = conn.get(f"{other_side}_part", "")
                board_pin_map.setdefault(other_part, []).append(pin)

    # Build setup/loop
    includes: list[str] = []
    setup_lines: list[str] = ["void setup() {", "  Serial.begin(9600);"]
    loop_lines: list[str] = ["void loop() {", "  // TODO: add your logic here"]

    for comp in components:
        comp_id = comp.get("id", "")
        comp_type = comp.get("type", "")
        if comp_id in board_pin_map:
            for pin in board_pin_map[comp_id]:
                template = COMPONENT_PIN_TEMPLATES.get(comp_type)
                if template:
                    setup_lines.append(f"  {template.format(pin=pin)}")

    setup_lines.append("}")
    loop_lines.append("}")

    lines = includes + [""] + setup_lines + [""] + loop_lines + [""]
    return "\n".join(lines)
