"""Device configuration loader.

This module provides functionality to load device definitions from JSON
configuration files, similar to how board configurations are loaded.
"""

import contextlib
import json
from pathlib import Path

from ..model import Device, DevicePin, PinRole, Point


def _get_device_config_path(config_name: str) -> Path:
    """
    Get the path to a device configuration file.

    Args:
        config_name: Device configuration name (without .json extension)

    Returns:
        Path to the configuration file
    """
    # Get the directory where this module is located
    module_dir = Path(__file__).parent.parent
    device_configs_dir = module_dir / "device_configs"

    # Search for the config file in subdirectories (sensors, leds, etc.)
    for category_dir in device_configs_dir.iterdir():
        if category_dir.is_dir():
            config_path = category_dir / f"{config_name}.json"
            if config_path.exists():
                return config_path

    # If not found in subdirectories, check root device_configs directory
    config_path = device_configs_dir / f"{config_name}.json"
    return config_path


def load_device_from_config(config_name: str, **parameters) -> Device:
    """
    Load a device definition from a JSON configuration file.

    This function reads a device configuration from the device_configs directory
    and returns a fully configured Device object with metadata.

    The configuration file must specify:
    - Device metadata (name, category, description, manufacturer, etc.)
    - Pin definitions with positions
    - Display properties (width, height, color)
    - Optional protocol and voltage information

    Args:
        config_name: Name of the device configuration file (without .json extension)
                    For example, "bh1750" will load "bh1750.json"
        **parameters: Optional parameters to customize the device (e.g., color_name for LEDs)

    Returns:
        Device: Configured device with all metadata and positioned pins

    Raises:
        FileNotFoundError: If the configuration file doesn't exist
        ValueError: If the configuration is invalid
        json.JSONDecodeError: If the JSON file is malformed

    Examples:
        >>> device = load_device_from_config("bh1750")
        >>> print(device.name)
        BH1750 Light Sensor
        >>> print(device.url)
        https://www.mouser.com/datasheet/2/348/bh1750fvi-e-186247.pdf

        >>> led = load_device_from_config("led", color_name="Blue")
        >>> print(led.name)
        Blue LED
    """
    config_path = _get_device_config_path(config_name)

    if not config_path.exists():
        raise FileNotFoundError(
            f"Device configuration file not found: {config_path}. "
            f"Available configurations should be placed in the device_configs directory."
        )

    try:
        with open(config_path) as f:
            config_dict = json.load(f)
    except json.JSONDecodeError as e:
        raise ValueError(f"Invalid JSON in device configuration file {config_path}: {e}") from e

    # Apply parameters if any are defined in the config
    device_name = config_dict.get("name", "Unknown Device")
    if "parameters" in config_dict:
        # Handle parameterized device names (e.g., "{color_name} LED")
        for param_name, param_config in config_dict["parameters"].items():
            if param_name in parameters:
                # Use provided parameter value
                param_value = parameters[param_name]
            else:
                # Use default from config
                param_value = param_config.get("default")

            # Apply parameter to device name
            device_name = device_name.replace(f"{{{param_name}}}", str(param_value))

    # Get layout configuration or use defaults
    layout_config = config_dict.get("layout", {})
    layout_type = layout_config.get("type", "vertical")  # vertical, horizontal, custom
    pin_spacing = layout_config.get("pin_spacing", 8.0)  # Default spacing
    pin_x = layout_config.get("pin_x", 5.0)  # Default x position
    start_y = layout_config.get("start_y", 10.0)  # Starting y position

    # Create DevicePin objects from configuration
    pins = []
    for index, pin_config in enumerate(config_dict.get("pins", [])):
        pin_role = PinRole(pin_config["role"])  # Convert string to PinRole enum

        # Auto-calculate position if not specified
        if "position" in pin_config:
            # Use explicit position if provided
            position_data = pin_config["position"]
            position = Point(position_data["x"], position_data["y"])
        else:
            # Auto-calculate based on layout type and pin order
            if layout_type == "vertical":
                position = Point(pin_x, start_y + (index * pin_spacing))
            elif layout_type == "horizontal":
                position = Point(pin_x + (index * pin_spacing), start_y)
            else:  # custom or fallback
                position = Point(pin_x, start_y + (index * pin_spacing))

        device_pin = DevicePin(
            name=pin_config["name"],
            role=pin_role,
            position=position,
        )
        pins.append(device_pin)

    # Get display properties with smart defaults
    display = config_dict.get("display", {})

    # Auto-calculate height based on pin count if not specified
    default_height = max(40.0, len(pins) * pin_spacing + 20)  # Fit all pins with padding
    width = display.get("width", 80.0)
    height = display.get("height", default_height)

    # Use category-based color defaults if not specified
    category = config_dict.get("category", "")
    category_colors = {
        "sensors": "#50E3C2",  # Turquoise
        "displays": "#4A90E2",  # Blue
        "leds": "#E74C3C",  # Red
        "actuators": "#F5A623",  # Orange
        "io": "#95A5A6",  # Gray
    }
    default_color = category_colors.get(category, "#4A90E2")
    color = display.get("color", default_color)

    # Parse I2C address if present (convert hex string to int)
    i2c_address = None
    if "i2c_address" in config_dict and config_dict["i2c_address"]:
        with contextlib.suppress(ValueError, TypeError):
            i2c_address = int(config_dict["i2c_address"], 16)

    # Create Device object with all metadata
    # Use 'id' field as type_id (primary), fall back to 'type_id' for backward compat
    type_id = config_dict.get("id") or config_dict.get("type_id")

    device = Device(
        name=device_name,
        pins=pins,
        width=width,
        height=height,
        color=color,
        type_id=type_id,
        description=config_dict.get("description"),
        url=config_dict.get("datasheet_url"),
        category=config_dict.get("category"),
        i2c_address=i2c_address,
    )

    return device
