from __future__ import annotations

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


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


def registry_targets(registry: dict[str, Any], *, status: str) -> set[str]:
    return {
        str(rule.get("target"))
        for rule in registry.get("rules") or []
        if isinstance(rule, dict) and rule.get("read_status") == status and rule.get("target")
    }


def has_zero_sample_named_range_verification(verification: dict[str, Any] | None) -> bool:
    if not verification:
        return False
    matched = False
    for result in verification.get("results") or []:
        if not isinstance(result, dict) or not str(result.get("target") or "").startswith("moxel.named_range."):
            continue
        matched = True
        for probe in result.get("probes") or []:
            if not isinstance(probe, dict):
                continue
            evidence = probe.get("evidence") if isinstance(probe.get("evidence"), dict) else {}
            if int(evidence.get("total") or 0) > 0:
                return False
    return matched


def build_plan(registry: dict[str, Any], verification: dict[str, Any] | None = None) -> dict[str, Any]:
    candidate_targets = registry_targets(registry, status="candidate_read")
    verified_targets = registry_targets(registry, status="verified_read")
    experiments: list[dict[str, Any]] = []

    named_range_targets = {target for target in candidate_targets if target.startswith("moxel.named_range.")}
    if named_range_targets:
        if has_zero_sample_named_range_verification(verification):
            experiments.append(
                {
                    "id": "named_range_presence_probe",
                    "goal": "Capture at least one decodable named range sample before splitting coordinate indexes.",
                    "target": "named_range",
                    "manual_action": "Create a visible named range `RANGE_RECT_TEST` through the 1C table named-area/range UI, save the template, and do not change cell text or formatting.",
                    "expected_signal": "The next probe should report `named_ranges` or `named_range_candidates` with name `RANGE_RECT_TEST` and non-empty raw_scalars.",
                    "rules_unblocked": sorted(named_range_targets),
                    "capture_command": "python scripts/watch_1c_moxel_property_experiment.py --property NamedRangePresence --target-name RANGE_RECT_TEST --target-text \"\" --timeout-seconds 600",
                }
            )
        experiments.extend(
            [
                {
                    "id": "named_range_rectangular_area",
                    "goal": "Split left/right and top/bottom raw scalar indexes.",
                    "target": "named_range",
                    "manual_action": "Create or move a named range `RANGE_RECT_TEST` to a non-square rectangle, for example R8C4:R10C7, without changing other cells.",
                    "expected_signal": "raw_scalars should contain distinct left, top, right, bottom values so candidate indexes can be assigned one-to-one.",
                    "rules_unblocked": sorted(named_range_targets),
                    "capture_command": "python scripts/watch_1c_moxel_property_experiment.py --property NamedRangeRectangle --target-name RANGE_RECT_TEST --target-text \"\" --timeout-seconds 600",
                },
                {
                    "id": "named_range_horizontal_resize",
                    "goal": "Separate right from left.",
                    "target": "named_range",
                    "manual_action": "Resize only the right edge of `RANGE_RECT_TEST`, for example R8C4:R10C7 -> R8C4:R10C9.",
                    "expected_signal": "Only the raw scalar index for right should change.",
                    "rules_unblocked": ["moxel.named_range.right"],
                    "capture_command": "python scripts/watch_1c_moxel_property_experiment.py --property NamedRangeRightResize --target-name RANGE_RECT_TEST --target-text \"\" --timeout-seconds 600",
                },
                {
                    "id": "named_range_vertical_resize",
                    "goal": "Separate bottom from top.",
                    "target": "named_range",
                    "manual_action": "Resize only the bottom edge of `RANGE_RECT_TEST`, for example R8C4:R10C7 -> R8C4:R12C7.",
                    "expected_signal": "Only the raw scalar index for bottom should change.",
                    "rules_unblocked": ["moxel.named_range.bottom"],
                    "capture_command": "python scripts/watch_1c_moxel_property_experiment.py --property NamedRangeBottomResize --target-name RANGE_RECT_TEST --target-text \"\" --timeout-seconds 600",
                },
            ]
        )

    property_plan = [
        ("cell_horizontal_align", "ГоризонтальноеПоложение", "Change only horizontal alignment on the tracked cell."),
        ("cell_vertical_align", "ВертикальноеПоложение", "Change only vertical alignment on the tracked cell."),
        ("cell_font_size", "Шрифт.Размер", "Change only font size on the tracked cell."),
        ("cell_text_color", "ЦветТекста", "Change only text color on the tracked cell."),
        ("column_width", "ШиринаКолонки", "Change only width of the tracked column."),
        ("row_height", "ВысотаСтроки", "Change only height of the tracked row."),
    ]
    for experiment_id, property_name, action in property_plan:
        experiments.append(
            {
                "id": experiment_id,
                "goal": f"Map MOXCEL scalar path for `{property_name}`.",
                "target": "cell_or_layout_property",
                "manual_action": action,
                "expected_signal": "One local candidate path should dominate in analyze_1c_moxel_property_experiments.py.",
                "rules_unblocked": [],
                "capture_command": f"python scripts/watch_1c_moxel_property_experiment.py --property {property_name} --target-text \"Ячейка 7 - 2\" --target-name R7C2_TEST --timeout-seconds 600",
            }
        )

    verification_summary = None
    if verification:
        verification_summary = verification.get("counts") or {}

    return {
        "schema": "codex_1c_moxel_next_experiments_plan.v1",
        "status": "ok",
        "basis": {
            "verified_targets": sorted(verified_targets),
            "candidate_targets": sorted(candidate_targets),
            "verification_counts": verification_summary,
        },
        "experiments": experiments,
        "counts": {
            "experiments": len(experiments),
            "named_range_experiments": sum(1 for item in experiments if item.get("target") == "named_range"),
            "property_experiments": sum(1 for item in experiments if item.get("target") == "cell_or_layout_property"),
        },
    }


def render_markdown(plan: dict[str, Any]) -> str:
    lines = ["# 1C MOXCEL Next Experiments", ""]
    counts = plan.get("counts") or {}
    lines.append(f"- Experiments: `{counts.get('experiments')}`")
    lines.append(f"- Named range: `{counts.get('named_range_experiments')}`")
    lines.append(f"- Properties: `{counts.get('property_experiments')}`")
    lines.append("")
    lines.append("| Experiment | Target | Goal |")
    lines.append("| --- | --- | --- |")
    for item in plan.get("experiments") or []:
        lines.append(f"| `{item.get('id')}` | `{item.get('target')}` | {item.get('goal')} |")
    lines.append("")
    lines.append("## Commands")
    lines.append("")
    for item in plan.get("experiments") or []:
        lines.append(f"- `{item.get('id')}`")
        lines.append("")
        lines.append("```powershell")
        lines.append(str(item.get("capture_command") or ""))
        lines.append("```")
        lines.append("")
    return "\n".join(lines)


def main() -> int:
    parser = argparse.ArgumentParser(description="Plan the next controlled 1C MOXCEL experiments from registry gaps.")
    parser.add_argument("--registry", default="plugins/1c/metadata/moxel-schema-registry.json")
    parser.add_argument("--verification", default="reports/1c-template-baselines/moxel-schema-registry-verification.json")
    parser.add_argument("--output-json", default="reports/1c-template-baselines/moxel-next-experiments.json")
    parser.add_argument("--output-markdown", default="reports/1c-template-baselines/moxel-next-experiments.md")
    args = parser.parse_args()

    registry = read_json(Path(args.registry))
    verification_path = Path(args.verification)
    verification = read_json(verification_path) if verification_path.exists() else None
    plan = build_plan(registry, verification)
    json_path = Path(args.output_json)
    md_path = Path(args.output_markdown)
    json_path.parent.mkdir(parents=True, exist_ok=True)
    md_path.parent.mkdir(parents=True, exist_ok=True)
    json_path.write_text(json.dumps(plan, ensure_ascii=False, indent=2), encoding="utf-8")
    md_path.write_text(render_markdown(plan), encoding="utf-8")
    print(json.dumps({"status": "ok", "json": str(json_path), "markdown": str(md_path), "counts": plan["counts"]}, ensure_ascii=False, indent=2))
    return 0


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