#!/usr/bin/env python3
"""Resolve composite primitive/reference value groups from a 1C SQL read result."""

from __future__ import annotations

import argparse
import json
from collections import defaultdict
from pathlib import Path
from typing import Any


SUFFIX_KIND = {
    "_TYPE": "type_marker",
    "_S": "string",
    "_N": "number",
    "_L": "boolean",
    "_T": "datetime",
    "_RRRef": "reference_id",
    "_RTRef": "reference_type",
}


def load_json(path: Path) -> dict[str, Any]:
    return json.loads(path.read_text(encoding="utf-8-sig"))


def value_hex(value: Any) -> str | None:
    if isinstance(value, dict) and value.get("kind") == "binary":
        return value.get("hex")
    return None


def is_zero_binary(value: Any) -> bool:
    hex_value = value_hex(value)
    return bool(hex_value) and set(hex_value) <= {"0"}


def is_present(value: Any) -> bool:
    if value is None:
        return False
    if isinstance(value, str):
        return value != ""
    if isinstance(value, dict) and value.get("kind") == "binary":
        return not is_zero_binary(value)
    return True


def column_suffix(column: str) -> str | None:
    for suffix in sorted(SUFFIX_KIND, key=len, reverse=True):
        if column.endswith(suffix):
            return suffix
    return None


def group_row(row: dict[str, Any]) -> dict[str, list[dict[str, Any]]]:
    groups: dict[str, list[dict[str, Any]]] = defaultdict(list)
    for cell_key, cell in row.items():
        column = str(cell.get("column") or "")
        suffix = column_suffix(column)
        if not suffix:
            continue
        metadata_path = str(cell.get("metadata_path") or cell_key)
        groups[metadata_path].append({"cell_key": cell_key, "suffix": suffix, "cell": cell})
    return groups


def reference_types(types: list[str]) -> list[str]:
    return [item for item in types if item.startswith("cfg:") and "Ref." in item]


def primitive_types(types: list[str]) -> list[str]:
    return [item for item in types if item.startswith("xs:")]


def selected_branch(parts: dict[str, dict[str, Any]], types: list[str]) -> dict[str, Any]:
    primitive_candidates = []
    for suffix in ("_S", "_N", "_L", "_T"):
        part = parts.get(suffix)
        if part and is_present(part["cell"].get("value")):
            primitive_candidates.append(
                {
                    "suffix": suffix,
                    "kind": SUFFIX_KIND[suffix],
                    "value": part["cell"].get("value"),
                }
            )
    rrref = parts.get("_RRRef")
    rtref = parts.get("_RTRef")
    if rrref and is_present(rrref["cell"].get("value")):
        refs = reference_types(types)
        return {
            "branch": "reference",
            "reference_id": rrref["cell"].get("value"),
            "reference_type_marker": rtref["cell"].get("value") if rtref else None,
            "candidate_reference_types": refs,
            "selection_evidence": "RRRef is non-zero; RTRef chooses target when present, otherwise value_type must contain a single reference type",
        }
    if primitive_candidates:
        return {
            "branch": "primitive",
            "primitive_values": primitive_candidates,
            "candidate_primitive_types": primitive_types(types),
            "selection_evidence": "primitive physical value column is present/non-empty",
        }
    return {
        "branch": "empty_or_unknown",
        "candidate_primitive_types": primitive_types(types),
        "candidate_reference_types": reference_types(types),
        "selection_evidence": "no non-empty primitive or reference value column observed",
    }


def collect_scope(rows: list[dict[str, Any]], *, scope: str, table_part_name: str | None, table_part_table: str | None) -> list[dict[str, Any]]:
    result = []
    for row_index, row in enumerate(rows):
        for metadata_path, members in group_row(row).items():
            if len(members) < 2:
                continue
            parts = {member["suffix"]: member for member in members}
            if "_TYPE" not in parts:
                continue
            first = members[0]["cell"]
            value_type = first.get("value_type") or {}
            types = list(value_type.get("types") or [])
            if len(types) < 2:
                continue
            result.append(
                {
                    "scope": scope,
                    "table_part_name": table_part_name,
                    "table_part_table": table_part_table,
                    "row_index": row_index,
                    "metadata_path": metadata_path,
                    "metadata_name": first.get("metadata_name"),
                    "metadata_uuid": first.get("metadata_uuid"),
                    "value_types": types,
                    "type_marker": parts["_TYPE"]["cell"].get("value"),
                    "physical_parts": {
                        suffix: {
                            "kind": SUFFIX_KIND[suffix],
                            "column": part["cell"].get("column"),
                            "cell_key": part["cell_key"],
                            "value": part["cell"].get("value"),
                        }
                        for suffix, part in sorted(parts.items())
                    },
                    "selected": selected_branch(parts, types),
                }
            )
    return result


def main() -> int:
    parser = argparse.ArgumentParser(description="Resolve composite primitive/reference SQL values.")
    parser.add_argument("--read-result", type=Path, required=True)
    parser.add_argument("--output", type=Path, required=True)
    args = parser.parse_args()

    read_result = load_json(args.read_result)
    composites = []
    composites.extend(
        collect_scope(
            read_result.get("main", {}).get("rows") or [],
            scope="main",
            table_part_name=None,
            table_part_table=read_result.get("main", {}).get("table"),
        )
    )
    for part in read_result.get("table_parts") or []:
        composites.extend(
            collect_scope(
                part.get("rows") or [],
                scope="table_part",
                table_part_name=part.get("name"),
                table_part_table=part.get("table"),
            )
        )

    summary = defaultdict(int)
    for item in composites:
        summary[item["selected"]["branch"]] += 1
    result = {
        "schema": "onec_sql_composite_value_resolution.v1",
        "read_result_path": str(args.read_result),
        "source": {"kind": read_result.get("kind"), "identity": read_result.get("identity")},
        "summary": {
            "composite_values": len(composites),
            "by_branch": dict(sorted(summary.items())),
        },
        "composites": composites,
    }
    args.output.parent.mkdir(parents=True, exist_ok=True)
    args.output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    print(json.dumps({"output": str(args.output), "summary": result["summary"]}, ensure_ascii=False))
    return 0


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