#!/usr/bin/env python3
"""Return module/file context for a 1C object with base/effective/extension views."""

from __future__ import annotations

import argparse
import base64
import json
import sys
from pathlib import Path
from typing import Any

sys.path.insert(0, str(Path(__file__).resolve().parent))
from resolve_1c_object import load_json, resolve_object  # noqa: E402


MODULE_PATTERNS = {
    "Ext/ObjectModule.bsl": ("object_module", "МодульОбъекта"),
    "Ext/ManagerModule.bsl": ("manager_module", "МодульМенеджера"),
}


def decode_arg(value: str | None, encoded: str | None) -> str | None:
    if encoded:
        return base64.b64decode(encoded).decode("utf-8")
    return value


def extension_name_from_path(path: str | None) -> str | None:
    parts = str(path or "").replace("/", "\\").split("\\")
    lowered = [part.casefold() for part in parts]
    for marker in ("расширения", "extensions"):
        if marker in lowered:
            index = lowered.index(marker)
            if index + 1 < len(parts):
                return parts[index + 1]
    return None


def module_record(path: Path, *, owner_dir: Path, source: str, extension_name: str | None = None) -> dict[str, Any]:
    relative = str(path.relative_to(owner_dir)).replace("\\", "/")
    kind, name = MODULE_PATTERNS.get(relative, ("module", path.stem))
    if relative.startswith("Forms/") and relative.endswith("/Ext/Form/Module.bsl"):
        form_name = relative.split("/", 2)[1]
        kind = "form_module"
        name = f"МодульФормы.{form_name}"
    elif relative.startswith("Commands/") and relative.endswith("/Ext/CommandModule.bsl"):
        command_name = relative.split("/", 2)[1]
        kind = "command_module"
        name = f"МодульКоманды.{command_name}"
    return {
        "name": name,
        "kind": kind,
        "relative_path": relative,
        "path": str(path),
        "size": path.stat().st_size,
        "origin": {"layer": source, **({"extension": extension_name} if extension_name else {})},
    }


def collect_modules(owner_xml_path: str | None, *, source: str, extension_name: str | None = None) -> list[dict[str, Any]]:
    if not owner_xml_path:
        return []
    owner_xml = Path(owner_xml_path)
    owner_dir = owner_xml.with_suffix("")
    if not owner_dir.is_dir():
        return []
    result = []
    for path in sorted(owner_dir.rglob("*.bsl")):
        result.append(module_record(path, owner_dir=owner_dir, source=source, extension_name=extension_name))
    return result


def merge_effective(base_modules: list[dict[str, Any]], extension_modules: list[dict[str, Any]]) -> list[dict[str, Any]]:
    result = []
    by_key: dict[tuple[str, str], dict[str, Any]] = {}
    for module in base_modules:
        copy = dict(module)
        copy["effective_action"] = "base"
        by_key[(copy["kind"], copy["name"])] = copy
        result.append(copy)
    for module in extension_modules:
        key = (module["kind"], module["name"])
        copy = dict(module)
        if key in by_key:
            copy["effective_action"] = "extended"
            by_key[key].setdefault("extension_overlays", []).append(copy)
        else:
            copy["effective_action"] = "added"
            result.append(copy)
    return result


def filter_extension(modules: list[dict[str, Any]], extension: str) -> list[dict[str, Any]]:
    result = []
    for module in modules:
        if (module.get("origin") or {}).get("extension") == extension:
            copy = dict(module)
            copy["effective_action"] = "extended_or_added"
            result.append(copy)
    return result


def build_context(index: dict[str, Any], *, kind: str, name: str, view: str, extension: str | None) -> dict[str, Any]:
    resolution = resolve_object(index, kind=kind, name=name, limit=100)
    canonical = resolution.get("canonical")
    if not canonical:
        raise SystemExit(f"Object not found: {kind}.{name}")
    base_modules = collect_modules(canonical.get("path"), source="base")

    extension_modules = []
    for overlay in resolution.get("extension_overlays") or []:
        overlay_extension = extension_name_from_path(overlay.get("path")) or overlay.get("extension_name")
        extension_modules.extend(collect_modules(overlay.get("path"), source="extension", extension_name=overlay_extension))

    if view == "base":
        modules = [{**module, "effective_action": "base"} for module in base_modules]
    elif view == "effective":
        modules = merge_effective(base_modules, extension_modules)
    elif view == "extension":
        if not extension:
            raise SystemExit("Use --extension with --view extension.")
        modules = filter_extension(extension_modules, extension)
    else:
        raise SystemExit(f"Unsupported view: {view}")

    return {
        "schema": "onec_object_code_context.v1",
        "view": view,
        "extension": extension if view == "extension" else None,
        "object": {
            "kind": canonical.get("kind"),
            "name": canonical.get("name"),
            "synonym": canonical.get("synonym"),
            "uuid": canonical.get("guid"),
        },
        "modules": modules,
        "resolution": {
            "schema": resolution.get("schema"),
            "canonical": canonical,
            "summary": resolution.get("summary"),
        },
        "counts": {
            "modules": len(modules),
            "base_modules": len(base_modules),
            "extension_modules": len(extension_modules),
        },
    }


def main() -> int:
    parser = argparse.ArgumentParser(description="Get 1C object code context.")
    parser.add_argument("--index", type=Path, required=True)
    parser.add_argument("--kind")
    parser.add_argument("--name")
    parser.add_argument("--kind-b64")
    parser.add_argument("--name-b64")
    parser.add_argument("--view", choices=["effective", "base", "extension"], default="effective")
    parser.add_argument("--extension")
    parser.add_argument("--output", type=Path)
    args = parser.parse_args()

    kind = decode_arg(args.kind, args.kind_b64)
    name = decode_arg(args.name, args.name_b64)
    if not kind or not name:
        raise SystemExit("Use --kind/--name or --kind-b64/--name-b64.")
    result = build_context(load_json(args.index), kind=kind, name=name, view=args.view, extension=args.extension)
    text = json.dumps(result, ensure_ascii=False, indent=2) + "\n"
    if args.output:
        args.output.parent.mkdir(parents=True, exist_ok=True)
        args.output.write_text(text, encoding="utf-8")
        print(json.dumps({"output": str(args.output), "counts": result["counts"], "view": result["view"]}, ensure_ascii=False))
    else:
        print(text)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
