"""
Schematic hierarchical-sheet commands.

Tools:
  - add_hierarchical_sheet:       insert a hierarchical-sheet reference into a parent schematic
  - create_hierarchical_subsheet: create a sub-sheet file and link it in one call

The command class holds a back-reference to KiCADInterface so it can reuse the existing
create_schematic handler, and exposes fix_subsheet_instances for the batch module to call.
"""

import logging
import re
import uuid
from pathlib import Path
from typing import Any, Dict, List

import sexpdata
from sexpdata import Symbol

logger = logging.getLogger("kicad_interface")


class SchematicHierarchyCommands:
    """Handlers for hierarchical sheet insertion and subsheet scaffolding."""

    def __init__(self, iface):
        self.iface = iface

    def add_hierarchical_sheet(self, params: Dict[str, Any]) -> Dict[str, Any]:
        """Insert a hierarchical sheet reference block into a parent schematic."""
        logger.info("Adding hierarchical sheet")
        try:
            schematic_path = params.get("schematicPath")
            subsheet_path = params.get("subsheetPath")
            sheet_name = params.get("sheetName", "Sheet")
            position = params.get("position", {})
            size = params.get("size", {})

            if not schematic_path or not subsheet_path:
                return {"success": False, "message": "schematicPath and subsheetPath are required"}

            x = float(position.get("x", 50))
            y = float(position.get("y", 50))
            w = float(size.get("width", 80))
            h = float(size.get("height", 50))

            parent_file = Path(schematic_path)
            try:
                rel_str = str(
                    Path(subsheet_path).resolve().relative_to(parent_file.parent.resolve())
                ).replace("\\", "/")
            except ValueError:
                rel_str = str(subsheet_path).replace("\\", "/")

            sheet_block_uuid = str(uuid.uuid4())
            name_x, name_y = round(x + 2.54, 4), round(y - 1.27, 4)
            file_x, file_y = round(x + 2.54, 4), round(y + h + 1.27, 4)

            sheet_block = (
                f"  (sheet (at {x} {y}) (size {w} {h}) (fields_autoplaced yes)\n"
                f"    (stroke (width 0.0006) (type default))\n"
                f"    (fill (color 0 0 0 0.0000))\n"
                f'    (uuid "{sheet_block_uuid}")\n'
                f'    (property "Sheet name" "{sheet_name}" (at {name_x} {name_y} 0)\n'
                f"      (effects (font (size 1.27 1.27)) (justify left bottom))\n"
                f"    )\n"
                f'    (property "Sheet file" "{rel_str}" (at {file_x} {file_y} 0)\n'
                f"      (effects (font (size 1.27 1.27)) (justify left bottom))\n"
                f"    )\n"
                f"  )\n"
            )

            content = parent_file.read_text(encoding="utf-8")
            parent_uuid_match = re.search(r"\(uuid\s+([0-9a-fA-F-]+)\)", content)
            parent_uuid = parent_uuid_match.group(1) if parent_uuid_match else ""
            existing_pages = re.findall(r'\(page\s+"(\d+)"\)', content)
            next_page = max((int(p) for p in existing_pages), default=0) + 1

            instance_path = (
                f"/{parent_uuid}/{sheet_block_uuid}" if parent_uuid else f"/{sheet_block_uuid}"
            )
            path_entry = f'    (path "{instance_path}" (page "{next_page}"))\n'

            insert_at = content.rfind("(sheet_instances")
            if insert_at == -1:
                return {"success": False, "message": "Could not find (sheet_instances in schematic"}
            content = content[:insert_at] + sheet_block + "  " + content[insert_at:]

            si_start = content.rfind("(sheet_instances")
            depth = 0
            si_close = len(content) - 1
            for i in range(si_start, len(content)):
                if content[i] == "(":
                    depth += 1
                elif content[i] == ")":
                    depth -= 1
                    if depth == 0:
                        si_close = i
                        break
            content = content[:si_close] + path_entry + "  " + content[si_close:]

            parent_file.write_text(content, encoding="utf-8")

            # Ensure each sub-sheet component has the hierarchical instance entry.
            self.fix_subsheet_instances(str(parent_file), content)

            return {
                "success": True,
                "sheet_uuid": sheet_block_uuid,
                "sheet_name": sheet_name,
                "subsheet_path": rel_str,
                "page": next_page,
            }

        except Exception as e:
            logger.error(f"Error adding hierarchical sheet: {e}")
            import traceback

            logger.error(traceback.format_exc())
            return {"success": False, "message": str(e)}

    def create_hierarchical_subsheet(self, params: Dict[str, Any]) -> Dict[str, Any]:
        """Create a new sub-sheet file and link it into a parent schematic in one call."""
        logger.info("Creating hierarchical subsheet")
        try:
            parent_path = params.get("parentSchematicPath")
            subsheet_path = params.get("subsheetPath")
            sheet_name = params.get("sheetName", "Sheet")
            position = params.get("position", {})
            size = params.get("size", {})
            metadata = params.get("metadata", {})

            if not parent_path or not subsheet_path:
                return {
                    "success": False,
                    "message": "parentSchematicPath and subsheetPath are required",
                }

            create_result = self.iface._handle_create_schematic(
                {"filename": subsheet_path, "metadata": metadata}
            )
            if not create_result.get("success"):
                return {
                    "success": False,
                    "message": f"Failed to create sub-sheet: {create_result.get('message')}",
                }

            link_result = self.add_hierarchical_sheet(
                {
                    "schematicPath": parent_path,
                    "subsheetPath": subsheet_path,
                    "sheetName": sheet_name,
                    "position": position,
                    "size": size,
                }
            )
            if not link_result.get("success"):
                return {
                    "success": False,
                    "message": f"Created sub-sheet but failed to link: {link_result.get('message')}",
                    "subsheet_created": create_result.get("file_path", subsheet_path),
                }

            return {
                "success": True,
                "subsheet_path": create_result.get("file_path", subsheet_path),
                "subsheet_uuid": create_result.get("schematic_uuid"),
                "sheet_block_uuid": link_result.get("sheet_uuid"),
                "sheet_name": sheet_name,
                "page": link_result.get("page"),
                "message": (
                    f"Created sub-sheet '{sheet_name}' at {subsheet_path} "
                    f"and linked it into {parent_path} (page {link_result.get('page')})"
                ),
            }
        except Exception as e:
            logger.error(f"Error in create_hierarchical_subsheet: {e}")
            import traceback

            logger.error(traceback.format_exc())
            return {"success": False, "message": str(e)}

    def fix_subsheet_instances(self, parent_path: str, parent_content: str) -> List[str]:
        """Ensure every component in each referenced sub-sheet has an instances entry for the
        sheet-block UUID, so ERC resolves references correctly. Returns modified sub-sheet paths.
        """
        modified_sheets: List[str] = []
        try:
            parent_file = Path(parent_path)
            parent_data = sexpdata.loads(parent_content)

            for item in parent_data:
                if not (isinstance(item, list) and len(item) > 0 and item[0] == Symbol("sheet")):
                    continue

                sheet_block_uuid = None
                sheet_file_rel = None
                for sub in item:
                    if isinstance(sub, list) and len(sub) >= 2 and sub[0] == Symbol("uuid"):
                        sheet_block_uuid = str(sub[1])
                    elif (
                        isinstance(sub, list)
                        and len(sub) >= 3
                        and sub[0] == Symbol("property")
                        and sub[1] == "Sheet file"
                    ):
                        sheet_file_rel = str(sub[2])
                if not sheet_block_uuid or not sheet_file_rel:
                    continue

                sub_sheet_path = parent_file.parent / sheet_file_rel
                if not sub_sheet_path.exists():
                    logger.warning(f"Sub-sheet not found: {sub_sheet_path}")
                    continue

                parent_uuid_match = re.search(r"\(uuid\s+([0-9a-fA-F-]+)\)", parent_content)
                parent_uuid = parent_uuid_match.group(1) if parent_uuid_match else ""
                target_path = (
                    f"/{parent_uuid}/{sheet_block_uuid}" if parent_uuid else f"/{sheet_block_uuid}"
                )

                sub_content = sub_sheet_path.read_text(encoding="utf-8")

                def _balanced_end(s: str, start: int) -> int:
                    depth = 0
                    for j in range(start, len(s)):
                        if s[j] == "(":
                            depth += 1
                        elif s[j] == ")":
                            depth -= 1
                            if depth == 0:
                                return j
                    return len(s) - 1

                result_parts: List[str] = []
                pos = 0
                changed = False
                while True:
                    idx = sub_content.find("(instances", pos)
                    if idx == -1:
                        result_parts.append(sub_content[pos:])
                        break
                    result_parts.append(sub_content[pos:idx])
                    end = _balanced_end(sub_content, idx)
                    block = sub_content[idx : end + 1]

                    if target_path not in block:
                        existing = re.search(r'\(reference\s+"([^"]+)"\)\s*\(unit\s+(\d+)\)', block)
                        if existing:
                            new_entry = (
                                f'(path "{target_path}" (reference "{existing.group(1)}") '
                                f"(unit {existing.group(2)}))"
                            )
                            proj_start = block.find("(project ")
                            if proj_start != -1:
                                proj_end = _balanced_end(block, proj_start)
                                block = block[:proj_end] + " " + new_entry + block[proj_end:]
                                changed = True
                    result_parts.append(block)
                    pos = end + 1

                if changed:
                    sub_sheet_path.write_text("".join(result_parts), encoding="utf-8")
                    modified_sheets.append(str(sub_sheet_path))
                    logger.info(
                        f"Fixed instances in {sub_sheet_path} for sheet-block {sheet_block_uuid}"
                    )

        except Exception as e:
            logger.error(f"Error fixing sub-sheet instances: {e}")
        return modified_sheets
