"""Robot Framework helper for UKO Layer 2 paradigm vocabulary tests.

Subcommands:
    vocab-oo           -- verify uko-oo: vocabulary, print ``vocab-oo-ok``
    vocab-func         -- verify uko-func: vocabulary, print ``vocab-func-ok``
    vocab-proc         -- verify uko-proc: vocabulary, print ``vocab-proc-ok``
    detail-map-chain   -- verify L2->L1->L0 chain, print ``detail-map-chain-ok``
    ttl-validate       -- validate TTL with new prefixes, print ``ttl-validate-ok``
"""

from __future__ import annotations

import sys
import traceback
from collections.abc import Callable
from pathlib import Path

_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
    sys.path.insert(0, _SRC)

from cleveragents.acms.uko.detail_level_maps import (  # noqa: E402
    CODE_DETAIL_LEVEL_MAP,
    FUNC_DETAIL_LEVEL_MAP,
    OO_DETAIL_LEVEL_MAP,
    PROC_DETAIL_LEVEL_MAP,
)
from cleveragents.acms.uko.vocabularies import (  # noqa: E402
    get_func_vocabulary,
    get_oo_vocabulary,
    get_proc_vocabulary,
)
from cleveragents.acms.uko.vocabulary_registry import VocabularyRegistry  # noqa: E402
from cleveragents.application.services.uko_loader import UKOLoader  # noqa: E402

_TTL_PATH = Path(__file__).resolve().parents[1] / "docs" / "ontology" / "uko.ttl"


def _cmd_vocab_oo() -> int:
    """Verify uko-oo: vocabulary has expected classes and properties."""
    try:
        vocab = get_oo_vocabulary()
        if len(vocab.classes) != 4:
            print(f"vocab-oo-fail: expected 4 classes, got {len(vocab.classes)}")
            return 1
        if len(vocab.properties) != 2:
            print(f"vocab-oo-fail: expected 2 properties, got {len(vocab.properties)}")
            return 1
        expected_classes = {"Class", "Interface", "Method", "Attribute"}
        actual_classes = {c.label for c in vocab.classes}
        if actual_classes != expected_classes:
            print(f"vocab-oo-fail: classes mismatch: {actual_classes}")
            return 1
        print(
            f"vocab-oo-ok: {len(vocab.classes)} classes, "
            f"{len(vocab.properties)} properties"
        )
        return 0
    except Exception as exc:  # robot helper must not crash
        traceback.print_exc()
        print(f"vocab-oo-fail: {exc}")
        return 1


def _cmd_vocab_func() -> int:
    """Verify uko-func: vocabulary has expected classes."""
    try:
        vocab = get_func_vocabulary()
        if len(vocab.classes) != 3:
            print(f"vocab-func-fail: expected 3 classes, got {len(vocab.classes)}")
            return 1
        expected = {"PureFunction", "TypeClass", "Monad"}
        actual = {c.label for c in vocab.classes}
        if actual != expected:
            print(f"vocab-func-fail: classes mismatch: {actual}")
            return 1
        print(f"vocab-func-ok: {len(vocab.classes)} classes")
        return 0
    except Exception as exc:  # robot helper must not crash
        traceback.print_exc()
        print(f"vocab-func-fail: {exc}")
        return 1


def _cmd_vocab_proc() -> int:
    """Verify uko-proc: vocabulary has expected classes."""
    try:
        vocab = get_proc_vocabulary()
        if len(vocab.classes) != 5:
            print(f"vocab-proc-fail: expected 5 classes, got {len(vocab.classes)}")
            return 1
        expected = {
            "ProceduralFunction",
            "GlobalVariable",
            "HeaderFile",
            "StructDefinition",
            "Macro",
        }
        actual = {c.label for c in vocab.classes}
        if actual != expected:
            print(f"vocab-proc-fail: classes mismatch: {actual}")
            return 1
        print(f"vocab-proc-ok: {len(vocab.classes)} classes")
        return 0
    except Exception as exc:  # robot helper must not crash
        traceback.print_exc()
        print(f"vocab-proc-fail: {exc}")
        return 1


def _cmd_detail_map_chain() -> int:
    """Verify DetailLevelMap inheritance chain: L2 -> L1 -> L0."""
    try:
        # Verify OO map extends CODE map
        oo_map = OO_DETAIL_LEVEL_MAP
        if oo_map.parent is None:
            print("detail-map-chain-fail: OO map has no parent")
            return 1
        if oo_map.parent.domain != "uko-code:":
            print(f"detail-map-chain-fail: OO parent is {oo_map.parent.domain}")
            return 1

        # Verify OO map has CLASS_HIERARCHY at depth 3
        depth = oo_map.resolve("CLASS_HIERARCHY")
        if depth != 3:
            print(f"detail-map-chain-fail: CLASS_HIERARCHY at {depth}, expected 3")
            return 1

        # Verify OO map has VISIBILITY_ANNOTATED at depth 7
        depth = oo_map.resolve("VISIBILITY_ANNOTATED")
        if depth != 7:
            print(f"detail-map-chain-fail: VISIBILITY_ANNOTATED at {depth}, expected 7")
            return 1

        # Verify FULL_SOURCE shifted to 11
        depth = oo_map.resolve("FULL_SOURCE")
        if depth != 11:
            print(f"detail-map-chain-fail: FULL_SOURCE at {depth}, expected 11")
            return 1

        # Verify func and proc maps have correct parent
        func_map = FUNC_DETAIL_LEVEL_MAP
        proc_map = PROC_DETAIL_LEVEL_MAP
        if func_map.parent is None or func_map.parent.domain != "uko-code:":
            print("detail-map-chain-fail: FUNC map parent incorrect")
            return 1
        if proc_map.parent is None or proc_map.parent.domain != "uko-code:":
            print("detail-map-chain-fail: PROC map parent incorrect")
            return 1

        # Verify CODE map has no parent
        code_map = CODE_DETAIL_LEVEL_MAP
        if code_map.parent is not None:
            print("detail-map-chain-fail: CODE map should have no parent")
            return 1

        print(
            f"detail-map-chain-ok: OO={len(oo_map.levels)} levels, "
            f"FUNC={len(func_map.levels)}, PROC={len(proc_map.levels)}, "
            f"CODE={len(code_map.levels)}"
        )
        return 0
    except Exception as exc:  # robot helper must not crash
        traceback.print_exc()
        print(f"detail-map-chain-fail: {exc}")
        return 1


def _cmd_ttl_validate() -> int:
    """Load and validate the updated TTL file with new prefixes."""
    try:
        loader = UKOLoader()
        ontology = loader.load(_TTL_PATH)
        prefixes = {p.prefix for p in ontology.prefixes}
        if "uko-func" not in prefixes:
            print("ttl-validate-fail: uko-func prefix missing")
            return 1
        if "uko-proc" not in prefixes:
            print("ttl-validate-fail: uko-proc prefix missing")
            return 1

        # Check layer 2 node count
        layer2 = [n for n in ontology.nodes if n.layer == 2]
        if len(layer2) < 12:
            print(f"ttl-validate-fail: expected >= 12 L2 nodes, got {len(layer2)}")
            return 1

        # Verify registry can be populated
        registry = VocabularyRegistry(
            vocabularies=(
                get_oo_vocabulary(),
                get_func_vocabulary(),
                get_proc_vocabulary(),
            )
        )
        if len(registry) != 3:
            print(f"ttl-validate-fail: registry has {len(registry)} vocabs")
            return 1

        print(
            f"ttl-validate-ok: {len(ontology.nodes)} nodes, "
            f"{len(layer2)} layer-2, {len(registry)} vocabs"
        )
        return 0
    except Exception as exc:  # robot helper must not crash
        traceback.print_exc()
        print(f"ttl-validate-fail: {exc}")
        return 1


_COMMANDS: dict[str, Callable[[], int]] = {
    "vocab-oo": _cmd_vocab_oo,
    "vocab-func": _cmd_vocab_func,
    "vocab-proc": _cmd_vocab_proc,
    "detail-map-chain": _cmd_detail_map_chain,
    "ttl-validate": _cmd_ttl_validate,
}


def main() -> int:
    """Entry point called by Robot Framework ``Run Process``."""
    if len(sys.argv) < 2:
        print(
            "Usage: helper_uko_layer2_paradigm.py "
            "<vocab-oo|vocab-func|vocab-proc|detail-map-chain|ttl-validate>"
        )
        return 1

    command = sys.argv[1]
    handler = _COMMANDS.get(command)
    if handler is None:
        print(f"Unknown command: {command}")
        return 1

    return handler()


if __name__ == "__main__":
    sys.exit(main())
