#!/usr/bin/env python3
"""Render 1C saved-state report delta JSON as Markdown."""

from __future__ import annotations

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


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


def line(text: str = "") -> str:
    return text.rstrip() + "\n"


def bullet(text: str) -> str:
    return f"- {text}\n"


def code(value: Any) -> str:
    if value is None:
        return "`null`"
    return f"`{value}`"


def term_list_delta(list_name: str, delta: dict[str, Any]) -> str:
    added = delta.get("added") or []
    removed = delta.get("removed") or []
    if not added and not removed:
        return ""
    out = ""
    if added:
        out += f"  New {list_name}: {', '.join(code(value) for value in added)}\n"
    if removed:
        out += f"  Cleared {list_name}: {', '.join(code(value) for value in removed)}\n"
    return out


def part_line(part: dict[str, Any]) -> str:
    return (
        f"{code(part.get('file_name'))}, "
        f"role={code(part.get('payload_role'))}, "
        f"active_exists={code(part.get('active_exists'))}, "
        f"summary={code(part.get('summary'))}, "
        f"delta_chars={code(part.get('delta_chars'))}"
    )


def render_object(item: dict[str, Any]) -> str:
    roles = sorted({
        str((part or {}).get("payload_role"))
        for part in item.get("parts") or []
        if (part or {}).get("payload_role")
    })
    out = bullet(
        f"{code(item.get('full_name'))}: "
        f"layer={code(item.get('layer'))}, "
        f"kind={code(item.get('kind_ru') or item.get('kind'))}, "
        f"parts={code(item.get('parts_count'))}, "
        f"roles={code(', '.join(roles))}"
    )
    added = item.get("added_terms") or []
    removed = item.get("removed_terms") or []
    if added:
        out += f"  Added terms: {', '.join(code(value) for value in added)}\n"
    if removed:
        out += f"  Removed terms: {', '.join(code(value) for value in removed)}\n"
    return out


def render_changed_object(change: dict[str, Any]) -> str:
    after = change.get("after") or {}
    out = bullet(
        f"{code(change.get('full_name'))}: "
        f"parts {code((change.get('before') or {}).get('parts_count'))}->{code(after.get('parts_count'))}, "
        f"text_diff_parts {code((change.get('before') or {}).get('text_diff_parts'))}->{code(after.get('text_diff_parts'))}, "
        f"active_missing_parts {code((change.get('before') or {}).get('active_missing_parts'))}->{code(after.get('active_missing_parts'))}"
    )
    term_delta = change.get("term_delta") or {}
    out += term_list_delta("added terms", term_delta.get("added_terms") or {})
    out += term_list_delta("removed terms", term_delta.get("removed_terms") or {})
    part_delta = change.get("part_delta") or {}
    for part in part_delta.get("added") or []:
        out += f"  Part added: {part_line(part)}\n"
    for part in part_delta.get("removed") or []:
        out += f"  Part removed: {part_line(part)}\n"
    for part in part_delta.get("changed") or []:
        before = part.get("before") or {}
        after_part = part.get("after") or {}
        out += (
            f"  Part changed: {code(part.get('file_name'))}, "
            f"role {code(before.get('payload_role'))}->{code(after_part.get('payload_role'))}, "
            f"active_exists {code(before.get('active_exists'))}->{code(after_part.get('active_exists'))}, "
            f"summary {code(before.get('summary'))}->{code(after_part.get('summary'))}, "
            f"delta_chars {code(before.get('delta_chars'))}->{code(after_part.get('delta_chars'))}\n"
        )
    return out


def render_system_changes(system: dict[str, Any]) -> str:
    out = ""
    added = system.get("added") or []
    removed = system.get("removed") or []
    changed = system.get("changed") or []
    if not added and not removed and not changed:
        return out
    out += line()
    out += line("## System Changes")
    for item in added:
        out += bullet(f"Added {code(item.get('name'))}: layer={code(item.get('layer'))}, extension={code(item.get('extension'))}")
    for item in removed:
        out += bullet(f"Removed {code(item.get('name'))}: layer={code(item.get('layer'))}, extension={code(item.get('extension'))}")
    for item in changed:
        after = item.get("after") or {}
        out += bullet(f"Changed {code(after.get('name') or item.get('key'))}: layer={code(after.get('layer'))}, extension={code(after.get('extension'))}")
    return out


def render(data: dict[str, Any]) -> str:
    counts = data.get("counts") or {}
    objects = data.get("objects") or {}
    out = ""
    out += line("# 1C Saved State Report Delta")
    out += line()
    out += line(f"Database: `{data.get('database')}`")
    out += line(f"Before: `{data.get('before_report')}`")
    out += line(f"After: `{data.get('after_report')}`")
    out += line()
    out += line("## Summary")
    for key in (
        "objects_added",
        "objects_removed",
        "objects_changed",
        "objects_unchanged",
        "system_added",
        "system_removed",
        "system_changed",
    ):
        out += bullet(f"{key}: `{counts.get(key)}`")

    added = objects.get("added") or []
    if added:
        out += line()
        out += line("## Added Objects")
        for item in added:
            out += render_object(item)

    removed = objects.get("removed") or []
    if removed:
        out += line()
        out += line("## Removed Objects")
        for item in removed:
            out += render_object(item)

    changed = objects.get("changed") or []
    if changed:
        out += line()
        out += line("## Changed Objects")
        for item in changed:
            out += render_changed_object(item)

    unchanged = objects.get("unchanged") or []
    if unchanged:
        out += line()
        out += line("## Unchanged Objects")
        for name in unchanged:
            out += bullet(code(name))

    out += render_system_changes(data.get("system_changes") or {})

    safety = data.get("safety") or {}
    out += line()
    out += line("## Safety")
    for key in ("read_only", "sql_write_performed", "public_terms_are_1c_objects"):
        out += bullet(f"{key}: `{safety.get(key)}`")
    return out


def main() -> int:
    parser = argparse.ArgumentParser(description="Render 1C saved-state report delta Markdown.")
    parser.add_argument("--delta", type=Path, required=True)
    parser.add_argument("--output", type=Path)
    args = parser.parse_args()

    markdown = render(load_json(args.delta))
    if args.output:
        args.output.parent.mkdir(parents=True, exist_ok=True)
        args.output.write_text(markdown, encoding="utf-8")
        print(json.dumps({"output": str(args.output)}, ensure_ascii=False))
    else:
        print(markdown)
    return 0


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