#!/usr/bin/env python3
"""Confronta due manifest JSON oscurando valori potenzialmente segreti."""

from __future__ import annotations

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


REDACTED = "[OSCURATO]"
SENSITIVE_PATTERN = re.compile(
    r"(^|_)(secret|password|passwd|token|api[_-]?key|private[_-]?key|access[_-]?key)(_|$)",
    re.IGNORECASE,
)


def redact(value: Any, key: str = "") -> Any:
    if SENSITIVE_PATTERN.search(key):
        return REDACTED
    if isinstance(value, dict):
        if value.get("secret") is True and "value" in value:
            return {name: (REDACTED if name == "value" else redact(item, name)) for name, item in value.items()}
        return {name: redact(item, name) for name, item in sorted(value.items())}
    if isinstance(value, list):
        cleaned = [redact(item) for item in value]
        if all(isinstance(item, dict) and ("id" in item or "name" in item) for item in cleaned):
            return sorted(cleaned, key=lambda item: str(item.get("id", item.get("name"))))
        return cleaned
    return value


def flatten(value: Any, path: str = "$") -> dict[str, Any]:
    result: dict[str, Any] = {}
    if isinstance(value, dict):
        for key, item in value.items():
            result.update(flatten(item, f"{path}.{key}"))
    elif isinstance(value, list):
        for index, item in enumerate(value):
            label = item.get("id", item.get("name", index)) if isinstance(item, dict) else index
            result.update(flatten(item, f"{path}[{label}]"))
    else:
        result[path] = value
    return result


def compare(old: Any, new: Any) -> dict[str, list[dict[str, Any]]]:
    old_flat = flatten(redact(old))
    new_flat = flatten(redact(new))
    added = [{"path": path, "value": new_flat[path]} for path in sorted(new_flat.keys() - old_flat.keys())]
    removed = [{"path": path, "value": old_flat[path]} for path in sorted(old_flat.keys() - new_flat.keys())]
    changed = [
        {"path": path, "before": old_flat[path], "after": new_flat[path]}
        for path in sorted(old_flat.keys() & new_flat.keys())
        if old_flat[path] != new_flat[path]
    ]
    return {"added": added, "removed": removed, "changed": changed}


def format_value(value: Any) -> str:
    return json.dumps(value, ensure_ascii=False)


def to_markdown(diff: dict[str, list[dict[str, Any]]], old_name: str, new_name: str) -> str:
    lines = [f"# Confronto workflow", "", f"- Prima: `{old_name}`", f"- Dopo: `{new_name}`", ""]
    sections = (("Aggiunti", "added"), ("Rimossi", "removed"), ("Modificati", "changed"))
    for title, key in sections:
        lines.extend([f"## {title}", ""])
        if not diff[key]:
            lines.extend(["Nessuno.", ""])
            continue
        for item in diff[key]:
            if key == "changed":
                lines.append(
                    f"- `{item['path']}`: {format_value(item['before'])} → {format_value(item['after'])}"
                )
            else:
                lines.append(f"- `{item['path']}`: {format_value(item['value'])}")
        lines.append("")
    lines.append("I campi riconosciuti come segreti sono oscurati automaticamente.")
    return "\n".join(lines) + "\n"


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("old", type=Path)
    parser.add_argument("new", type=Path)
    parser.add_argument("--output", type=Path)
    parser.add_argument("--json", action="store_true", dest="as_json")
    args = parser.parse_args()
    try:
        old = json.loads(args.old.read_text(encoding="utf-8"))
        new = json.loads(args.new.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError) as exc:
        print(f"Impossibile leggere i manifest: {exc}", file=sys.stderr)
        return 2
    diff = compare(old, new)
    output = (
        json.dumps(diff, ensure_ascii=False, indent=2) + "\n"
        if args.as_json
        else to_markdown(diff, args.old.name, args.new.name)
    )
    if args.output:
        args.output.parent.mkdir(parents=True, exist_ok=True)
        args.output.write_text(output, encoding="utf-8")
        print(f"Confronto scritto in {args.output}")
    else:
        print(output, end="")
    return 0


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