"""
EDA Tool Configuration — user-selectable toolchains for all three vendors.

Users pick tools per category (lint, simulation, coverage, formal, CDC, debug).
The selection is injected into every specialist prompt via {tool_context}.
The LLM uses bash() to run whatever tools are selected — it already knows
VCS, Xcelium, Questa, Verilator, SpyGlass, JasperGold, etc. from training data.

Default: Verilator (free, open-source, lint + simple sim).

Usage:
    config = ToolConfig()                              # all defaults (Verilator)
    config = ToolConfig(simulator="vcs", coverage="urg")  # Synopsys flow
    config = ToolConfig.from_file(".verifagent/tools.json")
    config = ToolConfig.preset("synopsys")             # full Synopsys suite

    context_str = build_tool_context(config)           # inject into prompts
"""

from __future__ import annotations

import json
import os
from dataclasses import dataclass, field, asdict
from pathlib import Path
from typing import Any, Dict, List, Optional


# =============================================================================
# Tool Metadata Registry
# =============================================================================

@dataclass(frozen=True)
class ToolInfo:
    """Metadata for a single EDA tool."""
    id: str
    display_name: str
    vendor: str
    category: str
    commands: Dict[str, str]
    common_flags: List[str] = field(default_factory=list)
    uvm_flags: List[str] = field(default_factory=list)
    coverage_flags: List[str] = field(default_factory=list)
    work_dir: str = ""
    error_example: str = ""
    env_hint: str = ""


TOOL_REGISTRY: Dict[str, ToolInfo] = {}


def _register(t: ToolInfo):
    TOOL_REGISTRY[t.id] = t


# ─── Lint / Static Analysis ──────────────────────────────────────────────────

_register(ToolInfo(
    id="verilator_lint",
    display_name="Verilator Lint",
    vendor="open-source",
    category="lint",
    commands={"lint": "verilator --lint-only -Wall -Wno-fatal"},
    common_flags=["-Wno-DECLFILENAME", "-Wno-UNUSEDPARAM", "-Wno-UNUSEDSIGNAL",
                  "-Wno-UNDRIVEN", "-Wno-PINCONNECTEMPTY", "-Wno-WIDTHEXPAND", "-Wno-WIDTHTRUNC"],
    work_dir="obj_dir",
    error_example="%Error-PINNOTFOUND: file.sv:42: Pin not found: 'data_out'",
))

_register(ToolInfo(
    id="spyglass",
    display_name="Synopsys SpyGlass",
    vendor="synopsys",
    category="lint",
    commands={"lint": "sg_shell -tcl", "batch": "spyglass -batch -goal lint/lint_rtl"},
    common_flags=["-sv", "-top", "-waiver"],
    work_dir="spyglass_results",
    error_example="Error: [LINT-1] file.sv(42): Signal 'data' is undriven",
    env_hint="Requires SNPSLMD_LICENSE_FILE or SPYGLASS_HOME set",
))

_register(ToolInfo(
    id="superlint",
    display_name="Cadence Jasper Superlint",
    vendor="cadence",
    category="lint",
    commands={"lint": "jg -superlint", "batch": "jg -batch superlint.tcl"},
    common_flags=["-sv09"],
    work_dir="jgproject",
    error_example="[SUPERLINT] ERROR: file.sv(42): combinational loop detected",
    env_hint="Requires CDS_LIC_FILE or JASPER_HOME set",
))

_register(ToolInfo(
    id="questa_lint",
    display_name="Siemens Questa Lint",
    vendor="siemens",
    category="lint",
    commands={"lint": "qverify -c -do 'lint run -d design_top; quit'"},
    common_flags=["-sv"],
    work_dir="qverify_results",
    error_example="** Error: (lint-42) file.sv(42): Latch inferred for signal 'data'",
    env_hint="Requires QUESTA_HOME or LM_LICENSE_FILE set",
))

# ─── Simulation ──────────────────────────────────────────────────────────────

_register(ToolInfo(
    id="verilator",
    display_name="Verilator",
    vendor="open-source",
    category="simulator",
    commands={
        "compile": "verilator --binary --timing -j 0",
        "run": "./obj_dir/V{top_module}",
        "lint": "verilator --lint-only -Wall",
    },
    common_flags=["-Wno-fatal", "--top-module {top_module}"],
    work_dir="obj_dir",
    error_example="%Error: file.sv:42: Cannot find: 'module_name'",
))

_register(ToolInfo(
    id="vcs",
    display_name="Synopsys VCS",
    vendor="synopsys",
    category="simulator",
    commands={
        "compile": "vcs -sverilog -full64 +v2k",
        "compile_uvm": "vcs -sverilog -full64 -ntb_opts uvm-1.2 +define+UVM_NO_DEPRECATED",
        "run": "./simv +UVM_TESTNAME={test_name} +UVM_VERBOSITY={verbosity}",
        "elaborate": None,
    },
    common_flags=["+acc+1", "-timescale=1ns/1ps", "-l compile.log"],
    uvm_flags=["-ntb_opts uvm-1.2", "+define+UVM_NO_DEPRECATED", "+incdir+{tb_dir}"],
    coverage_flags=["-cm line+cond+fsm+branch+tgl", "-cm_dir {coverage_dir}"],
    work_dir="csrc",
    error_example="Error-[SYNTAX] file.sv, 42: syntax error near 'endmodule'",
    env_hint="Requires VCS_HOME and SNPSLMD_LICENSE_FILE set",
))

_register(ToolInfo(
    id="xcelium",
    display_name="Cadence Xcelium",
    vendor="cadence",
    category="simulator",
    commands={
        "compile": "xrun -compile -sv -64bit -access +rwc",
        "compile_uvm": "xrun -compile -sv -uvm -64bit -access +rwc",
        "run": "xrun -R +UVM_TESTNAME={test_name} +UVM_VERBOSITY={verbosity}",
        "compile_and_run": "xrun -sv -uvm -64bit -access +rwc +UVM_TESTNAME={test_name}",
        "elaborate": None,
    },
    common_flags=["-timescale 1ns/1ps", "-l xrun.log", "+incdir+{tb_dir}"],
    uvm_flags=["-uvm", "+define+UVM_NO_DEPRECATED"],
    coverage_flags=["-coverage all", "-covoverwrite", "-covworkdir {coverage_dir}"],
    work_dir="xcelium.d",
    error_example="xmvlog: *E,SYNTAX (file.sv,42|23): syntax error",
    env_hint="Requires CDS_LIC_FILE and XCELIUMHOME set",
))

_register(ToolInfo(
    id="questa",
    display_name="Siemens Questa Sim",
    vendor="siemens",
    category="simulator",
    commands={
        "library": "vlib work && vmap work ./work",
        "compile": "vlog -sv +acc -work work",
        "compile_uvm": "vlog -sv +acc -mfcu -work work +cover=bcestf",
        "elaborate": "vopt -work work {top_module} -o {top_module}_opt +acc +cover=bcestf",
        "run": "vsim -c -coverage -work work {top_module}_opt +UVM_TESTNAME={test_name} +UVM_VERBOSITY={verbosity}",
    },
    common_flags=["-suppress 2167,2181,2263,2583,13314", "+incdir+{tb_dir}"],
    uvm_flags=["-L {uvm_lib}"],
    coverage_flags=["+cover=bcestf"],
    work_dir="questa_work",
    error_example="** Error: (vlog-2269) file.sv(42): Unterminated string literal",
    env_hint="Requires QUESTA_HOME or LM_LICENSE_FILE set",
))

# ─── Coverage ────────────────────────────────────────────────────────────────

_register(ToolInfo(
    id="no_coverage",
    display_name="None",
    vendor="none",
    category="coverage",
    commands={},
))

_register(ToolInfo(
    id="urg",
    display_name="Synopsys URG",
    vendor="synopsys",
    category="coverage",
    commands={
        "report": "urg -dir {vdb_dir} -report urgReport -format both",
        "merge": "urg -dir {vdb_dirs} -dbname merged.vdb",
    },
    work_dir="urgReport",
    error_example="URG: No coverage data found in directory",
))

_register(ToolInfo(
    id="imc",
    display_name="Cadence IMC",
    vendor="cadence",
    category="coverage",
    commands={
        "report": "imc -load {cov_dir} -exec 'report -metrics all -out imc_report.txt; exit'",
        "merge": "imc -merge {cov_dirs} -out merged_cov",
    },
    work_dir="cov_work",
    error_example="imc: *E: Cannot open coverage database",
))

_register(ToolInfo(
    id="vcover",
    display_name="Siemens vcover",
    vendor="siemens",
    category="coverage",
    commands={
        "report": "vcover report -details {ucdb_file}",
        "merge": "vcover merge -out merged.ucdb {ucdb_files}",
    },
    work_dir="questa_work",
    error_example="** Error: (vcover-110) Cannot open UCDB file",
))

# ─── Formal Verification ────────────────────────────────────────────────────

_register(ToolInfo(
    id="no_formal",
    display_name="None",
    vendor="none",
    category="formal",
    commands={},
))

_register(ToolInfo(
    id="vc_formal",
    display_name="Synopsys VC Formal",
    vendor="synopsys",
    category="formal",
    commands={
        "run": "vcf -f formal.tcl",
        "batch": "vcf -batch -f formal.tcl",
    },
    common_flags=["-sverilog", "-full64"],
    work_dir="vcf_work",
    env_hint="Requires VCS_HOME and SNPSLMD_LICENSE_FILE set",
))

_register(ToolInfo(
    id="jasper",
    display_name="Cadence JasperGold",
    vendor="cadence",
    category="formal",
    commands={
        "run": "jg -batch formal.tcl",
        "fpv": "jg -fpv",
    },
    common_flags=["-sv09"],
    work_dir="jgproject",
    env_hint="Requires CDS_LIC_FILE or JASPER_HOME set",
))

_register(ToolInfo(
    id="questa_formal",
    display_name="Siemens Questa Formal",
    vendor="siemens",
    category="formal",
    commands={
        "run": "qverify -c -do 'formal compile; formal verify -all; quit'",
    },
    work_dir="qverify_results",
    env_hint="Requires QUESTA_HOME or LM_LICENSE_FILE set",
))

# ─── AMD/Xilinx Vivado ───────────────────────────────────────────────────────

_register(ToolInfo(
    id="vivado_lint",
    display_name="Vivado Lint (xvlog)",
    vendor="amd-xilinx",
    category="lint",
    commands={"lint": "xvlog --sv {files}", "batch": "xvlog --sv --relax -f filelist.f"},
    common_flags=["--sv", "--relax", "-L uvm"],
    work_dir="xsim.dir",
    error_example="ERROR: [VRFC 10-2263] file.sv(42): syntax error near 'endmodule'",
    env_hint="Requires Vivado on PATH (source /tools/Xilinx/Vivado/2025.1/settings64.sh)",
))

_register(ToolInfo(
    id="xsim",
    display_name="AMD Vivado XSIM",
    vendor="amd-xilinx",
    category="simulator",
    commands={
        "compile": "xvlog --sv --relax",
        "compile_uvm": "xvlog --sv --relax -L uvm --include {tb_dir}",
        "elaborate": "xelab {top_module} -timescale 1ns/1ps -snapshot {top_module}_snap -debug typical",
        "elaborate_uvm": "xelab {top_module} -L uvm -timescale 1ns/1ps -snapshot {top_module}_snap -debug typical -covergroupmerge",
        "elaborate_coverage": "xelab {top_module} -L uvm -timescale 1ns/1ps -snapshot {top_module}_snap -debug typical -cc_type sbct -covergroupmerge",
        "run": "xsim {top_module}_snap -runall",
        "run_uvm": "xsim {top_module}_snap -testplusarg UVM_TESTNAME={test_name} -testplusarg UVM_VERBOSITY={verbosity} -runall",
        "run_coverage": "xsim {top_module}_snap -testplusarg UVM_TESTNAME={test_name} -testplusarg UVM_VERBOSITY={verbosity} -cov_db_name {test_name} -runall",
        "run_tcl": "xsim {top_module}_snap -tclbatch run.tcl",
    },
    common_flags=["--relax", "-L uvm"],
    uvm_flags=["-L uvm", "--include {tb_dir}"],
    coverage_flags=["-cc_type sbct", "-covergroupmerge", "-cov_db_name {test_name}"],
    work_dir="xsim.dir",
    error_example="ERROR: [VRFC 10-2263] file.sv(42): syntax error near 'endmodule'",
    env_hint="Requires Vivado on PATH (source settings64.sh). Free with Vivado ML Edition. UVM 1.2 built-in.",
))

_register(ToolInfo(
    id="xcrg",
    display_name="AMD Vivado XCRG",
    vendor="amd-xilinx",
    category="coverage",
    commands={
        "report": "xcrg -report_format html -dir {coverage_dir} -report_dir xcrg_report",
        "merge": "xcrg -merge_dir {coverage_dirs} -dir merged_cov",
        "summary": "xcrg -report_format text -dir {coverage_dir}",
    },
    work_dir="xcrg_report",
    error_example="ERROR: [xcrg-1] Cannot open coverage database",
    env_hint="Part of Vivado installation. Generates HTML coverage reports.",
))

_register(ToolInfo(
    id="vivado_synth",
    display_name="Vivado Synthesis",
    vendor="amd-xilinx",
    category="lint",
    commands={
        "lint": "vivado -mode batch -source synth_check.tcl",
        "synth": "vivado -mode batch -source run_synth.tcl",
    },
    common_flags=["-mode batch", "-nojournal", "-nolog"],
    work_dir="vivado_project",
    error_example="ERROR: [Synth 8-87] file.sv(42): cannot resolve module 'my_module'",
    env_hint="Requires Vivado on PATH. Use for FPGA synthesis checks.",
))

# ─── CDC (Clock Domain Crossing) ────────────────────────────────────────────

_register(ToolInfo(
    id="no_cdc",
    display_name="None",
    vendor="none",
    category="cdc",
    commands={},
))

_register(ToolInfo(
    id="spyglass_cdc",
    display_name="Synopsys SpyGlass CDC",
    vendor="synopsys",
    category="cdc",
    commands={"run": "sg_shell -tcl cdc.tcl", "batch": "spyglass -batch -goal cdc/cdc_verify_struct"},
    env_hint="Requires SPYGLASS_HOME set",
))

_register(ToolInfo(
    id="conformal_cdc",
    display_name="Cadence Conformal CDC",
    vendor="cadence",
    category="cdc",
    commands={"run": "lec -cdc -dofile cdc.do"},
    env_hint="Requires CDS_LIC_FILE set",
))

_register(ToolInfo(
    id="questa_cdc",
    display_name="Siemens Questa CDC",
    vendor="siemens",
    category="cdc",
    commands={"run": "qverify -c -do 'cdc run -d design_top; quit'"},
    env_hint="Requires QUESTA_HOME set",
))

# ─── Equivalence Checking ────────────────────────────────────────────────────

_register(ToolInfo(
    id="no_equiv",
    display_name="None",
    vendor="none",
    category="equiv",
    commands={},
))

_register(ToolInfo(
    id="conformal_lec",
    display_name="Cadence Conformal LEC",
    vendor="cadence",
    category="equiv",
    commands={
        "run": "lec -dofile ec.do",
        "batch": "lec -xl -nogui -dofile ec.do",
    },
    common_flags=["-sv"],
    work_dir="lec_work",
    error_example="// ERROR: (LEC-200) Unable to read reference design",
    env_hint="Requires CDS_LIC_FILE set",
))

_register(ToolInfo(
    id="formality",
    display_name="Synopsys Formality",
    vendor="synopsys",
    category="equiv",
    commands={
        "run": "fm_shell -f formality.tcl",
        "batch": "fm_shell -f formality.tcl",
    },
    common_flags=["-sverilog"],
    work_dir="fm_work",
    error_example="Error: Cannot read design file 'design.sv'",
    env_hint="Requires SNPSLMD_LICENSE_FILE and FM_HOME set",
))

_register(ToolInfo(
    id="questa_ec",
    display_name="Siemens Questa EC",
    vendor="siemens",
    category="equiv",
    commands={
        "run": "qverify -c -do 'ec run; quit'",
    },
    work_dir="qverify_results",
    error_example="** Error: (ec-42) Unable to map point 'data_out'",
    env_hint="Requires QUESTA_HOME or LM_LICENSE_FILE set",
))

# ─── Debug / Waveform ───────────────────────────────────────────────────────

_register(ToolInfo(
    id="no_debug",
    display_name="None",
    vendor="none",
    category="debug",
    commands={},
))

_register(ToolInfo(
    id="verdi",
    display_name="Synopsys Verdi",
    vendor="synopsys",
    category="debug",
    commands={"open": "verdi -sv -f filelist.f -ssf {waveform}", "dump_flag": "-kdb"},
    env_hint="Requires VERDI_HOME and SNPSLMD_LICENSE_FILE set",
))

_register(ToolInfo(
    id="simvision",
    display_name="Cadence SimVision",
    vendor="cadence",
    category="debug",
    commands={"open": "simvision {waveform}", "dump_flag": "-access +rwc -input shm.tcl"},
    env_hint="Requires CDS_LIC_FILE set",
))

_register(ToolInfo(
    id="questa_viz",
    display_name="Siemens Questa Visualizer",
    vendor="siemens",
    category="debug",
    commands={"open": "visualizer {waveform}", "dump_flag": "+acc"},
    env_hint="Requires QUESTA_HOME set",
))


# =============================================================================
# User Tool Configuration
# =============================================================================

TOOL_CATEGORIES = ["lint", "simulator", "coverage", "formal", "cdc", "equiv", "debug"]

CATEGORY_DEFAULTS = {
    "lint": "verilator_lint",
    "simulator": "verilator",
    "coverage": "no_coverage",
    "formal": "no_formal",
    "cdc": "no_cdc",
    "equiv": "no_equiv",
    "debug": "no_debug",
}

VENDOR_PRESETS = {
    "open_source": {
        "lint": "verilator_lint",
        "simulator": "verilator",
        "coverage": "no_coverage",
        "formal": "no_formal",
        "cdc": "no_cdc",
        "equiv": "no_equiv",
        "debug": "no_debug",
    },
    "synopsys": {
        "lint": "spyglass",
        "simulator": "vcs",
        "coverage": "urg",
        "formal": "vc_formal",
        "cdc": "spyglass_cdc",
        "equiv": "formality",
        "debug": "verdi",
    },
    "cadence": {
        "lint": "superlint",
        "simulator": "xcelium",
        "coverage": "imc",
        "formal": "jasper",
        "cdc": "conformal_cdc",
        "equiv": "conformal_lec",
        "debug": "simvision",
    },
    "siemens": {
        "lint": "questa_lint",
        "simulator": "questa",
        "coverage": "vcover",
        "formal": "questa_formal",
        "cdc": "questa_cdc",
        "equiv": "questa_ec",
        "debug": "questa_viz",
    },
    "amd_xilinx": {
        "lint": "vivado_lint",
        "simulator": "xsim",
        "coverage": "xcrg",
        "formal": "no_formal",
        "cdc": "no_cdc",
        "equiv": "no_equiv",
        "debug": "no_debug",
    },
}


@dataclass
class ToolConfig:
    """User's EDA tool selection with versions. Persisted to .verifagent/tools.json.

    The versions dict maps tool_id -> version string (e.g. "2024.1", "T-2022.03").
    Version info is injected into prompts so the LLM generates version-appropriate
    commands. This matters because:
      - Cadence pre-2018 uses irun/ncsim (Incisive), post-2018 uses xrun (Xcelium)
      - Vivado pre-2019.1 has no UVM in XSIM, pre-2020.1 has no coverage
      - Siemens ModelSim (old) has no UVM/SVA/coverage vs QuestaSim (full)
      - VCS/SpyGlass flag syntax evolves across versions
    """
    lint: str = "verilator_lint"
    simulator: str = "verilator"
    coverage: str = "no_coverage"
    formal: str = "no_formal"
    cdc: str = "no_cdc"
    equiv: str = "no_equiv"
    debug: str = "no_debug"
    versions: Dict[str, str] = field(default_factory=dict)
    execution_mode: str = "cloud"  # "cloud" = run on server, "local" = run on user's machine via CLI

    def set_version(self, tool_id: str, version: str):
        """Set the version for a specific tool."""
        self.versions[tool_id] = version

    def get_version(self, tool_id: str) -> str:
        """Get version for a tool, or empty string if not set."""
        return self.versions.get(tool_id, "")

    @classmethod
    def preset(cls, vendor: str) -> "ToolConfig":
        """Create a config from a vendor preset."""
        if vendor not in VENDOR_PRESETS:
            raise ValueError(f"Unknown preset '{vendor}'. Options: {list(VENDOR_PRESETS.keys())}")
        return cls(**VENDOR_PRESETS[vendor])

    @classmethod
    def from_file(cls, path: str = ".verifagent/tools.json") -> "ToolConfig":
        """Load config from a JSON file. Returns defaults if file missing."""
        p = Path(path)
        if p.exists():
            try:
                data = json.loads(p.read_text())
                kwargs = {k: data[k] for k in CATEGORY_DEFAULTS if k in data}
                missing = [k for k in CATEGORY_DEFAULTS if k not in data]
                if missing:
                    import logging as _logging
                    _logging.getLogger(__name__).debug(
                        "tools.json missing keys %s — using defaults", missing
                    )
                if "versions" in data and isinstance(data["versions"], dict):
                    kwargs["versions"] = data["versions"]
                if "execution_mode" in data and data["execution_mode"] in ("cloud", "local"):
                    kwargs["execution_mode"] = data["execution_mode"]
                return cls(**kwargs)
            except Exception as e:
                import logging as _logging
                _logging.getLogger(__name__).warning(
                    "Failed to load tool config from %s: %s — using defaults", path, e
                )
        return cls()

    def save(self, path: str = ".verifagent/tools.json"):
        """Persist config to JSON."""
        p = Path(path)
        p.parent.mkdir(parents=True, exist_ok=True)
        p.write_text(json.dumps(asdict(self), indent=2) + "\n")

    def get_tool(self, category: str) -> ToolInfo:
        """Get the ToolInfo for a category."""
        tool_id = getattr(self, category, CATEGORY_DEFAULTS.get(category, ""))
        return TOOL_REGISTRY.get(tool_id, TOOL_REGISTRY[CATEGORY_DEFAULTS[category]])

    def summary(self) -> str:
        """Human-readable summary of selections."""
        lines = []
        for cat in TOOL_CATEGORIES:
            tool = self.get_tool(cat)
            ver = self.get_version(tool.id)
            ver_str = f" v{ver}" if ver else ""
            lines.append(f"  {cat:12s}: {tool.display_name}{ver_str} ({tool.vendor})")
        return "\n".join(lines)


# =============================================================================
# Prompt Context Builder
# =============================================================================

def build_tool_context(config: ToolConfig) -> str:
    """Build the {tool_context} string for injection into specialist prompts.

    This is the ONLY thing specialists need to see. It tells the LLM what
    tools are available, what commands to use, and how to interpret errors.
    The LLM runs everything through bash() and adapts from raw output.
    """
    lint = config.get_tool("lint")
    sim = config.get_tool("simulator")
    cov = config.get_tool("coverage")
    formal = config.get_tool("formal")
    cdc = config.get_tool("cdc")
    equiv = config.get_tool("equiv")
    debug = config.get_tool("debug")

    def _ver(tool_id: str) -> str:
        v = config.get_version(tool_id)
        return f" version {v}" if v else ""

    sections = []

    sections.append("## EDA Tool Configuration")
    sections.append("")

    # Lint
    sections.append(f"LINT TOOL: {lint.display_name}{_ver(lint.id)} ({lint.vendor})")
    if lint.commands:
        sections.append(f"  Command: {lint.commands.get('lint', lint.commands.get('batch', ''))}")
        if lint.common_flags:
            sections.append(f"  Common flags: {' '.join(lint.common_flags)}")
        if lint.error_example:
            sections.append(f"  Error format example: {lint.error_example}")
    sections.append("")

    # Simulator
    sections.append(f"SIMULATOR: {sim.display_name}{_ver(sim.id)} ({sim.vendor})")
    for cmd_name, cmd_val in sim.commands.items():
        if cmd_val:
            sections.append(f"  {cmd_name}: {cmd_val}")
    if sim.uvm_flags:
        sections.append(f"  UVM flags: {' '.join(sim.uvm_flags)}")
    if sim.coverage_flags:
        sections.append(f"  Coverage flags: {' '.join(sim.coverage_flags)}")
    if sim.error_example:
        sections.append(f"  Error format example: {sim.error_example}")
    if sim.work_dir:
        sections.append(f"  Work directory: {sim.work_dir}/")
    if sim.env_hint:
        sections.append(f"  Environment: {sim.env_hint}")
    sections.append("")

    # Coverage
    if cov.commands:
        sections.append(f"COVERAGE TOOL: {cov.display_name}{_ver(cov.id)} ({cov.vendor})")
        for cmd_name, cmd_val in cov.commands.items():
            sections.append(f"  {cmd_name}: {cmd_val}")
        if cov.work_dir:
            sections.append(f"  Work directory: {cov.work_dir}/")
        sections.append("")

    # Formal
    if formal.commands:
        sections.append(f"FORMAL TOOL: {formal.display_name}{_ver(formal.id)} ({formal.vendor})")
        for cmd_name, cmd_val in formal.commands.items():
            sections.append(f"  {cmd_name}: {cmd_val}")
        sections.append("")

    # CDC
    if cdc.commands:
        sections.append(f"CDC TOOL: {cdc.display_name}{_ver(cdc.id)} ({cdc.vendor})")
        for cmd_name, cmd_val in cdc.commands.items():
            sections.append(f"  {cmd_name}: {cmd_val}")
        sections.append("")

    # Equivalence Checking
    if equiv.commands:
        sections.append(f"EQUIV CHECK TOOL: {equiv.display_name}{_ver(equiv.id)} ({equiv.vendor})")
        for cmd_name, cmd_val in equiv.commands.items():
            sections.append(f"  {cmd_name}: {cmd_val}")
        if equiv.env_hint:
            sections.append(f"  Environment: {equiv.env_hint}")
        sections.append("")

    # Debug
    if debug.commands:
        sections.append(f"DEBUG/WAVEFORM: {debug.display_name}{_ver(debug.id)} ({debug.vendor})")
        for cmd_name, cmd_val in debug.commands.items():
            sections.append(f"  {cmd_name}: {cmd_val}")
        sections.append("")

    # General guidance
    # Execution mode
    if config.execution_mode == "local":
        sections.append("EXECUTION MODE: LOCAL (commands run on user's machine via CLI)")
        sections.append("  All bash() commands are forwarded to the user's machine for execution.")
        sections.append("  The user has the EDA tools installed locally with valid licenses.")
        sections.append("  Commands run in the user's project directory. Results stream back to you.")
        sections.append("")

    sections.append("TOOL USAGE RULES:")
    sections.append("- Use the tools listed above for all compilation, simulation, and analysis.")
    sections.append("- Run tools via the bash() tool. Read error output carefully — each tool has")
    sections.append("  its own error format (examples shown above).")
    sections.append("- Do NOT use tools from other vendors unless explicitly configured above.")
    sections.append(f"- Build artifacts go in: {sim.work_dir}/ — do NOT edit files in that directory.")
    if cov.commands:
        sections.append(f"- Coverage artifacts go in: {cov.work_dir}/ — use the coverage tool to read them.")
    sections.append("- If a tool is not installed or fails with 'command not found', report it clearly")
    sections.append("  and suggest the user check their PATH and license configuration.")
    sections.append("- IMPORTANT: Tool versions matter. Commands and flags can change between versions.")
    sections.append("  Use version-appropriate syntax for the versions listed above. Key differences:")
    sections.append("    Cadence pre-2018: irun/ncsim (Incisive) NOT xrun (Xcelium)")
    sections.append("    Vivado pre-2019.1: XSIM has no UVM support (-L uvm unavailable)")
    sections.append("    Vivado pre-2020.1: XSIM has no code coverage (no xcrg)")
    sections.append("    Siemens ModelSim: no UVM, no SVA, no coverage (use QuestaSim instead)")
    sections.append("  If the version is not specified above, use the latest stable syntax.")

    return "\n".join(sections)


def get_available_tools(category: Optional[str] = None) -> List[ToolInfo]:
    """List all registered tools, optionally filtered by category."""
    tools = list(TOOL_REGISTRY.values())
    if category:
        tools = [t for t in tools if t.category == category]
    return tools


def get_vendor_tools(vendor: str) -> List[ToolInfo]:
    """List all tools from a specific vendor."""
    return [t for t in TOOL_REGISTRY.values() if t.vendor == vendor]
