"""Handler for browsing and searching KiCad symbol libraries."""

import os
import re
import sys
from pathlib import Path


def handle_list_symbol_libraries(kicad_info: dict, args: dict) -> dict:
    """List all symbol libraries registered in KiCad's sym-lib-table."""
    sys.path.insert(0, str(Path(__file__).parent.parent))
    from lib_table import LibTable

    sym_lib_table = kicad_info.get("sym_lib_table")
    if not sym_lib_table:
        return {"success": False, "error": "KiCad sym-lib-table path not found"}

    try:
        table = LibTable.parse_file(sym_lib_table)
    except Exception as e:
        return {"success": False, "error": f"Failed to parse sym-lib-table: {e}"}

    libraries = []
    for entry in table.entries:
        # Resolve ${KICAD9_SYMBOL_DIR} and similar env vars
        uri = _resolve_kicad_vars(entry.uri, kicad_info)
        exists = Path(uri).exists() if uri else False
        libraries.append({
            "name": entry.name,
            "type": entry.lib_type,
            "uri": entry.uri,
            "resolved_path": uri,
            "exists": exists,
            "description": entry.descr,
        })

    return {
        "success": True,
        "output": f"Found {len(libraries)} symbol libraries",
        "data": {"libraries": libraries},
    }


def handle_search_symbols(kicad_info: dict, args: dict) -> dict:
    """Search across all symbol libraries by name/keyword.

    Args (in args dict):
        query: Search term (regex supported)
        limit: Max results (default 50)
    """
    sys.path.insert(0, str(Path(__file__).parent.parent))
    from lib_table import LibTable
    from parsers.sexpr import parse_file, find_nodes, find_node, node_value

    query = args.get("query", "")
    if not query:
        return {"success": False, "error": "No query specified"}

    limit = int(args.get("limit", 50))

    sym_lib_table = kicad_info.get("sym_lib_table")
    if not sym_lib_table:
        return {"success": False, "error": "KiCad sym-lib-table path not found"}

    try:
        table = LibTable.parse_file(sym_lib_table)
    except Exception as e:
        return {"success": False, "error": f"Failed to parse sym-lib-table: {e}"}

    try:
        pattern = re.compile(query, re.IGNORECASE)
    except re.error as e:
        return {"success": False, "error": f"Invalid regex pattern: {e}"}

    results = []
    searched = 0

    for entry in table.entries:
        uri = _resolve_kicad_vars(entry.uri, kicad_info)
        if not uri or not Path(uri).exists():
            continue

        searched += 1

        try:
            tree = parse_file(uri)
        except Exception:
            continue

        for sym in find_nodes(tree, "symbol"):
            if len(sym) < 2 or not isinstance(sym[1], str):
                continue
            sym_name = sym[1]

            # Skip internal sub-symbols (e.g., "OpAmp_0_1", "OpAmp_1_1")
            if re.match(r'.*_\d+_\d+$', sym_name):
                continue

            # Check name match
            if not pattern.search(sym_name):
                # Also check Description and keywords properties
                desc_prop = find_node(sym, "property")
                match_found = False
                for child in sym:
                    if isinstance(child, list) and child and child[0] == "property":
                        prop_name = child[1] if len(child) > 1 else ""
                        prop_val = child[2] if len(child) > 2 else ""
                        if prop_name in ("Description", "ki_keywords") and isinstance(prop_val, str):
                            if pattern.search(prop_val):
                                match_found = True
                                break
                if not match_found:
                    continue

            # Extract symbol info
            props = {}
            for child in sym:
                if isinstance(child, list) and child and child[0] == "property" and len(child) >= 3:
                    props[child[1]] = child[2]

            # Count pins (follow extends chain if needed)
            pin_count = _count_pins(sym, tree)

            results.append({
                "library": entry.name,
                "name": sym_name,
                "description": props.get("Description", ""),
                "footprint": props.get("Footprint", ""),
                "datasheet": props.get("Datasheet", ""),
                "keywords": props.get("ki_keywords", ""),
                "pin_count": pin_count,
            })

            if len(results) >= limit:
                break

        if len(results) >= limit:
            break

    return {
        "success": True,
        "output": f"Found {len(results)} symbol(s) matching '{query}' (searched {searched} libraries)",
        "data": {
            "results": results,
            "query": query,
            "libraries_searched": searched,
            "truncated": len(results) >= limit,
        },
    }


def handle_get_symbol_info(kicad_info: dict, args: dict) -> dict:
    """Get detailed info about a specific symbol.

    Args:
        library: Library name
        symbol: Symbol name
    """
    sys.path.insert(0, str(Path(__file__).parent.parent))
    from lib_table import LibTable
    from parsers.sexpr import parse_file, find_node, find_nodes, node_value

    library = args.get("library", "")
    symbol = args.get("symbol", "")
    if not library:
        return {"success": False, "error": "No library specified"}
    if not symbol:
        return {"success": False, "error": "No symbol specified"}

    sym_lib_table = kicad_info.get("sym_lib_table")
    if not sym_lib_table:
        return {"success": False, "error": "KiCad sym-lib-table path not found"}

    try:
        table = LibTable.parse_file(sym_lib_table)
    except Exception as e:
        return {"success": False, "error": f"Failed to parse sym-lib-table: {e}"}

    # Find the library entry
    lib_entry = None
    for entry in table.entries:
        if entry.name == library:
            lib_entry = entry
            break

    if not lib_entry:
        return {
            "success": False,
            "error": f"Library '{library}' not found",
            "_hint": "Call list_symbol_libraries to see valid library names.",
        }

    uri = _resolve_kicad_vars(lib_entry.uri, kicad_info)
    if not uri or not Path(uri).exists():
        return {"success": False, "error": f"Library file not found: {lib_entry.uri}"}

    try:
        tree = parse_file(uri)
    except Exception as e:
        return {"success": False, "error": f"Failed to parse library: {e}"}

    # Find the symbol
    sym_node = None
    for sym in find_nodes(tree, "symbol"):
        if len(sym) >= 2 and sym[1] == symbol:
            sym_node = sym
            break

    if not sym_node:
        return {
            "success": False,
            "error": f"Symbol '{symbol}' not found in library '{library}'",
            "_hint": "Call search_symbols to find the correct symbol name, or list_library_symbols to enumerate symbols in this library.",
        }

    # Extract properties
    props = {}
    for child in sym_node:
        if isinstance(child, list) and child and child[0] == "property" and len(child) >= 3:
            props[child[1]] = child[2]

    # Extract pins from sub-symbols (follow extends chain if needed)
    pins = _extract_pins(sym_node, tree)

    # Check flags
    is_power = any(
        (isinstance(c, list) and c == ["power"]) or c == "power"
        for c in sym_node
    )

    return {
        "success": True,
        "output": f"Symbol '{symbol}' from '{library}': {len(pins)} pin(s)",
        "data": {
            "library": library,
            "name": symbol,
            "properties": props,
            "pins": pins,
            "pin_count": len(pins),
            "is_power": is_power,
            "description": props.get("Description", ""),
            "footprint": props.get("Footprint", ""),
            "datasheet": props.get("Datasheet", ""),
        },
    }


def handle_search_footprints(kicad_info: dict, args: dict) -> dict:
    """Search across all footprint libraries by name/keyword.

    Args (in args dict):
        query: Search term (regex supported)
        limit: Max results (default 50)
    """
    sys.path.insert(0, str(Path(__file__).parent.parent))
    from lib_table import LibTable
    from parsers.sexpr import parse_file, find_node, find_nodes, node_value

    query = args.get("query", "")
    if not query:
        return {"success": False, "error": "No query specified"}

    limit = int(args.get("limit", 50))

    fp_lib_table = kicad_info.get("fp_lib_table")
    if not fp_lib_table:
        return {"success": False, "error": "KiCad fp-lib-table path not found"}

    try:
        table = LibTable.parse_file(fp_lib_table)
    except Exception as e:
        return {"success": False, "error": f"Failed to parse fp-lib-table: {e}"}

    try:
        pattern = re.compile(query, re.IGNORECASE)
    except re.error as e:
        return {"success": False, "error": f"Invalid regex pattern: {e}"}

    results = []
    searched = 0

    # Build list of (library_name, pretty_dir_path) to search.
    # Includes both fp-lib-table entries AND system footprint libraries.
    lib_dirs = []
    for entry in table.entries:
        uri = _resolve_kicad_vars(entry.uri, kicad_info)
        if uri:
            lib_dirs.append((entry.name, Path(uri)))

    # Also include system footprint libraries (not always in user fp-lib-table)
    base_dir = kicad_info.get("base_dir", "")
    if base_dir:
        sys_fp_dir = Path(base_dir) / "share" / "kicad" / "footprints"
        if sys_fp_dir.is_dir():
            seen = {d.name for _, d in lib_dirs}
            for pretty in sorted(sys_fp_dir.iterdir()):
                if pretty.suffix == ".pretty" and pretty.name not in seen:
                    lib_name = pretty.stem  # e.g. "Capacitor_SMD"
                    lib_dirs.append((lib_name, pretty))

    for lib_name, pretty_dir in lib_dirs:
        if not pretty_dir.is_dir():
            continue

        searched += 1

        # Check if the library name itself matches (all footprints qualify)
        lib_match = pattern.search(lib_name)

        for mod_file in sorted(pretty_dir.glob("*.kicad_mod")):
            fp_name = mod_file.stem  # filename without .kicad_mod

            # Fast match: filename or library name only (no file parse)
            if not (pattern.search(fp_name) or lib_match):
                continue

            # Only parse the file for metadata on matches (fast path)
            descr = ""
            tags = ""
            pad_count = 0
            fp_type = ""
            try:
                tree = parse_file(str(mod_file))
                descr_node = find_node(tree, "descr")
                tags_node = find_node(tree, "tags")
                descr = node_value(descr_node) or "" if descr_node else ""
                tags = node_value(tags_node) or "" if tags_node else ""
                pad_count = len(find_nodes(tree, "pad"))
                attr_node = find_node(tree, "attr")
                if attr_node:
                    fp_type = node_value(attr_node) or ""
            except Exception:
                pass

            results.append({
                "library": lib_name,
                "name": fp_name,
                "description": descr,
                "tags": tags,
                "pad_count": pad_count,
                "type": fp_type,
            })

            if len(results) >= limit:
                break

        if len(results) >= limit:
            break

    return {
        "success": True,
        "output": f"Found {len(results)} footprint(s) matching '{query}' (searched {searched} libraries)",
        "data": {
            "results": results,
            "query": query,
            "libraries_searched": searched,
            "truncated": len(results) >= limit,
        },
    }


def _count_pins(sym_node: list, tree: list) -> int:
    """Count pins in a symbol, following extends chains."""
    from parsers.sexpr import find_node, find_nodes, node_value

    # Check for extends
    extends_node = find_node(sym_node, "extends")
    if extends_node:
        parent_name = node_value(extends_node)
        if parent_name:
            for sym in find_nodes(tree, "symbol"):
                if len(sym) >= 2 and sym[1] == parent_name:
                    return _count_pins(sym, tree)
        return 0

    count = 0
    for child in sym_node:
        if isinstance(child, list) and child and child[0] == "symbol":
            count += len(find_nodes(child, "pin"))
    return count


def _extract_pins(sym_node: list, tree: list) -> list[dict]:
    """Extract pins from a symbol, following extends chains."""
    from parsers.sexpr import find_node, find_nodes, node_value

    # Check for extends
    extends_node = find_node(sym_node, "extends")
    if extends_node:
        parent_name = node_value(extends_node)
        if parent_name:
            for sym in find_nodes(tree, "symbol"):
                if len(sym) >= 2 and sym[1] == parent_name:
                    return _extract_pins(sym, tree)
        return []

    pins = []
    for child in sym_node:
        if isinstance(child, list) and child and child[0] == "symbol":
            for pin in find_nodes(child, "pin"):
                if len(pin) >= 3:
                    pin_type = pin[1] if isinstance(pin[1], str) else ""
                    pin_style = pin[2] if len(pin) > 2 and isinstance(pin[2], str) else ""
                    name_node = find_node(pin, "name")
                    number_node = find_node(pin, "number")
                    pins.append({
                        "type": pin_type,
                        "style": pin_style,
                        "name": node_value(name_node) or "",
                        "number": node_value(number_node) or "",
                    })
    return pins


def _resolve_kicad_vars(uri: str, kicad_info: dict) -> str:
    """Resolve KiCad environment variables in URIs.

    e.g., ${KICAD9_SYMBOL_DIR}/Device.kicad_sym
    """
    if "${" not in uri:
        return uri

    base_dir = kicad_info.get("base_dir", "")

    # Common KiCad environment variables
    replacements = {
        "KICAD9_SYMBOL_DIR": os.path.join(base_dir, "share", "kicad", "symbols") if base_dir else "",
        "KICAD8_SYMBOL_DIR": os.path.join(base_dir, "share", "kicad", "symbols") if base_dir else "",
        "KICAD7_SYMBOL_DIR": os.path.join(base_dir, "share", "kicad", "symbols") if base_dir else "",
        "KICAD_SYMBOL_DIR": os.path.join(base_dir, "share", "kicad", "symbols") if base_dir else "",
        "KICAD9_FOOTPRINT_DIR": os.path.join(base_dir, "share", "kicad", "footprints") if base_dir else "",
        "KICAD8_FOOTPRINT_DIR": os.path.join(base_dir, "share", "kicad", "footprints") if base_dir else "",
        "KICAD7_FOOTPRINT_DIR": os.path.join(base_dir, "share", "kicad", "footprints") if base_dir else "",
        "KICAD_FOOTPRINT_DIR": os.path.join(base_dir, "share", "kicad", "footprints") if base_dir else "",
        "KICAD9_3DMODEL_DIR": os.path.join(base_dir, "share", "kicad", "3dmodels") if base_dir else "",
        "KICAD8_3DMODEL_DIR": os.path.join(base_dir, "share", "kicad", "3dmodels") if base_dir else "",
        "KICAD7_3DMODEL_DIR": os.path.join(base_dir, "share", "kicad", "3dmodels") if base_dir else "",
    }

    # Also check OS environment variables
    for var_name in re.findall(r'\$\{(\w+)\}', uri):
        if var_name not in replacements:
            env_val = os.environ.get(var_name, "")
            if env_val:
                replacements[var_name] = env_val

    result = uri
    for var_name, var_value in replacements.items():
        result = result.replace(f"${{{var_name}}}", var_value)

    return result
