from __future__ import annotations

import csv
import json
from pathlib import Path
from typing import Dict, List, Tuple

from _pipeline_utils import _generated_timestamp

WEIGHT_GLOBAL = 0.60
WEIGHT_SURA = 0.25
WEIGHT_VERSE = 0.15


def _slugify(name: str) -> str:
    return "".join(ch.lower() if ch.isalnum() else "_" for ch in name).strip("_")


def _write_per_dataset_grand_summaries(root: Path, generated_at: str, datasets: List[Dict[str, object]]) -> None:
    base_dir = root / "summary" / "grand"
    json_dir = base_dir / "json"
    csv_dir = base_dir / "csv"
    txt_dir = base_dir / "txt"
    json_dir.mkdir(parents=True, exist_ok=True)
    csv_dir.mkdir(parents=True, exist_ok=True)
    txt_dir.mkdir(parents=True, exist_ok=True)

    for item in datasets:
        name = str(item["mushaf"])
        slug = _slugify(name)
        verse = dict(item["verse"])
        sura = dict(item["sura"])
        glob = dict(item["global"])
        verse_metric = dict(verse["locks_by_metric"])
        sura_metric = dict(sura["locks_by_metric"])
        family = dict(glob["family_passes"])

        # 1) JSON grand summary per dataset
        json_payload = {
            "schema_version": "1.0",
            "summary_type": "dataset_grand_summary",
            "generated": generated_at,
            "mushaf": name,
            "verse": verse,
            "sura": sura,
            "global": glob,
        }
        with (json_dir / f"grand_summary_{slug}.json").open("w", encoding="utf-8") as handle:
            json.dump(json_payload, handle, ensure_ascii=True, indent=2)

        # 2) CSV grand summary per dataset (single normalized row)
        csv_row = {
            "mushaf": name,
            "generated": generated_at,
            "verse_verses_scanned": int(verse["verses_scanned"]),
            "verse_total_metric_locks": int(verse["total_metric_locks"]),
            "sura_suras_scanned": int(sura["suras_scanned"]),
            "sura_total_metric_locks": int(sura["total_metric_locks"]),
            "global_pass_measurements": int(glob["pass_measurements"]),
            "global_fail_measurements": int(glob["fail_measurements"]),
            "global_total_measurements": int(glob["total_measurements"]),
            "global_all_measurements_passed": bool(glob["all_measurements_passed"]),
            "global_has_failures": bool(glob["has_failures"]),
        }
        for metric in ["M1", "M2", "M3", "M4", "M5", "M6", "M7"]:
            csv_row[f"verse_{metric}"] = int(verse_metric.get(metric, 0))
            csv_row[f"sura_{metric}"] = int(sura_metric.get(metric, 0))
        for fam in ["A", "B", "C", "D", "E", "F", "G"]:
            csv_row[f"global_family_{fam}"] = int(family.get(fam, 0))

        with (csv_dir / f"grand_summary_{slug}.csv").open("w", encoding="utf-8", newline="") as handle:
            writer = csv.DictWriter(handle, fieldnames=list(csv_row.keys()))
            writer.writeheader()
            writer.writerow(csv_row)

        # 3) TXT grand summary per dataset
        txt_lines = [
            "GRAND SUMMARY",
            f"Generated: {generated_at}",
            f"Mushaf: {name}",
            "",
            "Verse:",
            f"- Verses scanned: {verse['verses_scanned']}",
            f"- Total metric locks: {verse['total_metric_locks']}",
            "- Locks by metric: " + ", ".join(f"{m}={verse_metric.get(m, 0)}" for m in ["M1", "M2", "M3", "M4", "M5", "M6", "M7"]),
            "",
            "Sura:",
            f"- Suras scanned: {sura['suras_scanned']}",
            f"- Total metric locks: {sura['total_metric_locks']}",
            "- Locks by metric: " + ", ".join(f"{m}={sura_metric.get(m, 0)}" for m in ["M1", "M2", "M3", "M4", "M5", "M6", "M7"]),
            "",
            "Global:",
            f"- Pass measurements: {glob['pass_measurements']}/{glob['total_measurements']}",
            f"- Fail measurements: {glob['fail_measurements']}/{glob['total_measurements']}",
            "- Family passes: " + ", ".join(f"{fam}={family.get(fam, 0)}" for fam in ["A", "B", "C", "D", "E", "F", "G"]),
            "",
        ]
        (txt_dir / f"grand_summary_{slug}.txt").write_text("\n".join(txt_lines), encoding="utf-8")


def parse_verse_summary(path: Path) -> Dict[str, Dict[str, object]]:
    payload = json.loads(path.read_text(encoding="utf-8"))
    datasets = payload.get("datasets", [])
    out: Dict[str, Dict[str, object]] = {}
    for item in datasets:
        name = str(item["mushaf"])
        verses_scanned = int(item["verses_scanned"])
        total_locks = int(item["total_metric_locks"])
        locks_by_metric = {
            str(k): int(v)
            for k, v in dict(item.get("locks_by_metric", {})).items()
        }
        out[name] = {
            "verses_scanned": verses_scanned,
            "total_locks": total_locks,
            "locks_by_metric": locks_by_metric,
        }

    return out


def parse_sura_summary(path: Path) -> Dict[str, Dict[str, object]]:
    payload = json.loads(path.read_text(encoding="utf-8"))
    datasets = payload.get("datasets", [])
    out: Dict[str, Dict[str, object]] = {}
    for item in datasets:
        name = str(item["mushaf"])
        suras_scanned = int(item["suras_scanned"])
        total_locks = int(item["total_metric_locks"])
        locks_by_metric = {
            str(k): int(v)
            for k, v in dict(item.get("locks_by_metric", {})).items()
        }
        out[name] = {
            "suras_scanned": suras_scanned,
            "total_locks": total_locks,
            "locks_by_metric": locks_by_metric,
        }

    return out


def parse_global_summary(path: Path) -> Dict[str, Dict[str, object]]:
    payload = json.loads(path.read_text(encoding="utf-8"))
    datasets = payload.get("datasets", [])
    out: Dict[str, Dict[str, object]] = {}
    for item in datasets:
        name = str(item["mushaf"])
        metrics = dict(item.get("metrics", {}))
        g = dict(item.get("G", {}))

        family_passes = {"A": 0, "B": 0, "C": 0, "D": 0, "E": 0, "F": 0, "G": 0}
        pass_count = int(item.get("pass_count", 0))
        fail_count = int(item.get("fail_count", 0))

        for metric_name in metrics:
            metric_payload = dict(metrics[metric_name])
            for family in ["A", "B", "C", "D", "E", "F"]:
                if bool(dict(metric_payload.get(family, {})).get("is_multiple_of_19", False)):
                    family_passes[family] += 1

        if bool(g.get("is_multiple_of_19", False)):
            family_passes["G"] = 1

        if pass_count + fail_count != 43:
            raise ValueError(
                f"Global measurement count mismatch for {name}: {pass_count + fail_count}"
            )

        out[name] = {
            "pass": pass_count,
            "fail": fail_count,
            "family_passes": family_passes,
        }

    return out


def _score(global_pass: int, sura_locks: int, suras_scanned: int, verse_locks: int, verses_scanned: int) -> float:
    global_rate = global_pass / 43.0
    sura_density = sura_locks / float(suras_scanned)
    verse_density = verse_locks / float(verses_scanned)
    return (
        WEIGHT_GLOBAL * global_rate
        + WEIGHT_SURA * sura_density
        + WEIGHT_VERSE * verse_density
    )


def build_locks_summary(root: Path, output_path: Path) -> None:
    verse_path = root / "summary" / "json" / "grand_verse_lock_summary.json"
    sura_path = root / "summary" / "json" / "grand_sura_lock_summary.json"
    global_path = root / "summary" / "json" / "global_lock_summary.json"

    verse_data = parse_verse_summary(verse_path)
    sura_data = parse_sura_summary(sura_path)
    global_data = parse_global_summary(global_path)

    all_names = sorted(set(verse_data) | set(sura_data) | set(global_data))
    if set(verse_data) != set(sura_data) or set(verse_data) != set(global_data):
        raise ValueError(
            "Dataset mismatch across summaries: "
            f"verse_only={sorted(set(verse_data) - set(sura_data) - set(global_data))}, "
            f"sura_only={sorted(set(sura_data) - set(verse_data) - set(global_data))}, "
            f"global_only={sorted(set(global_data) - set(verse_data) - set(sura_data))}"
        )

    lines: List[str] = []
    bool_lines: List[str] = []
    generated_at = _generated_timestamp()
    lines.append("LOCKS")
    lines.append(f"Generated: {generated_at}")
    lines.append("Scope: Combined generated summary of Verse, Sura, and Global locks by mushaf.")
    lines.append("")
    bool_lines.append("LOCKS (BOOLEAN)")
    bool_lines.append(f"Generated: {generated_at}")
    bool_lines.append("Scope: Combined generated summary of Verse, Sura, and Global locks by mushaf.")
    bool_lines.append("")

    ranking_rows: List[Tuple[str, float, int, int, int]] = []
    dataset_rows: List[Dict[str, object]] = []

    for name in all_names:
        verse = verse_data[name]
        sura = sura_data[name]
        glob = global_data[name]

        verse_metrics = verse["locks_by_metric"]
        sura_metrics = sura["locks_by_metric"]
        family = glob["family_passes"]

        # Numeric lines
        lines.append(name)
        lines.append("Verse Lock:")
        lines.append(f"- Verses scanned: {verse['verses_scanned']}")
        lines.append(f"- Total metric locks: {verse['total_locks']}")
        lines.append(
            "- Locks by metric: "
            + ", ".join(f"{k.split('_', 1)[0]}={verse_metrics[k]}" for k in sorted(verse_metrics))
        )
        lines.append("Sura Lock:")
        lines.append(f"- Suras scanned: {sura['suras_scanned']}")
        lines.append(f"- Total metric locks: {sura['total_locks']}")
        lines.append(
            "- Locks by metric: "
            + ", ".join(f"{k.split('_', 1)[0]}={sura_metrics[k]}" for k in sorted(sura_metrics))
        )
        lines.append("Global Lock:")
        lines.append(f"- Pass measurements: {glob['pass']}/43")
        lines.append(f"- Fail measurements: {glob['fail']}/43")
        lines.append(
            "- Family passes: "
            + ", ".join(f"{fam}={family[fam]}" for fam in ["A", "B", "C", "D", "E", "F", "G"])
        )
        lines.append("")

        # Boolean lines
        bool_lines.append(name)
        bool_lines.append("Verse Lock:")
        bool_lines.append(f"- Verses scanned: {verse['verses_scanned']}")
        bool_lines.append(f"- Total metric locks: {verse['total_locks'] > 0}")
        bool_lines.append(
            "- Locks by metric: "
            + ", ".join(f"{k.split('_', 1)[0]}={verse_metrics[k] > 0}" for k in sorted(verse_metrics))
        )
        bool_lines.append("Sura Lock:")
        bool_lines.append(f"- Suras scanned: {sura['suras_scanned']}")
        bool_lines.append(f"- Total metric locks: {sura['total_locks'] > 0}")
        bool_lines.append(
            "- Locks by metric: "
            + ", ".join(f"{k.split('_', 1)[0]}={sura_metrics[k] > 0}" for k in sorted(sura_metrics))
        )
        bool_lines.append("Global Lock:")
        bool_lines.append(f"- All measurements passed: {glob['pass'] == 43}")
        bool_lines.append(f"- Has failures: {glob['fail'] > 0}")
        bool_lines.append(
            "- Family passes: "
            + ", ".join(f"{fam}={family[fam] > 0}" for fam in ["A", "B", "C", "D", "E", "F", "G"])
        )
        bool_lines.append("")

        dataset_rows.append(
            {
                "mushaf": name,
                "verse": {
                    "verses_scanned": verse["verses_scanned"],
                    "total_metric_locks": verse["total_locks"],
                    "total_metric_locks_bool": verse["total_locks"] > 0,
                    "locks_by_metric": {k.split('_', 1)[0]: verse_metrics[k] for k in sorted(verse_metrics)},
                    "locks_by_metric_bool": {k.split('_', 1)[0]: verse_metrics[k] > 0 for k in sorted(verse_metrics)},
                },
                "sura": {
                    "suras_scanned": sura["suras_scanned"],
                    "total_metric_locks": sura["total_locks"],
                    "total_metric_locks_bool": sura["total_locks"] > 0,
                    "locks_by_metric": {k.split('_', 1)[0]: sura_metrics[k] for k in sorted(sura_metrics)},
                    "locks_by_metric_bool": {k.split('_', 1)[0]: sura_metrics[k] > 0 for k in sorted(sura_metrics)},
                },
                "global": {
                    "pass_measurements": glob["pass"],
                    "fail_measurements": glob["fail"],
                    "total_measurements": 43,
                    "all_measurements_passed": glob["pass"] == 43,
                    "has_failures": glob["fail"] > 0,
                    "family_passes": {fam: family[fam] for fam in ["A", "B", "C", "D", "E", "F", "G"]},
                    "family_passes_bool": {fam: family[fam] > 0 for fam in ["A", "B", "C", "D", "E", "F", "G"]},
                },
            }
        )

        score = _score(
            global_pass=glob["pass"],
            sura_locks=sura["total_locks"],
            suras_scanned=sura["suras_scanned"],
            verse_locks=verse["total_locks"],
            verses_scanned=verse["verses_scanned"],
        )
        ranking_rows.append((name, score, glob["pass"], sura["total_locks"], verse["total_locks"]))

    ranking_rows.sort(key=lambda item: item[1], reverse=True)

    lines.append("Deterministic Ranking (Overall):")
    lines.append(
        "Scoring formula: "
        "0.60*GlobalPassRate + 0.25*SuraLockDensity + 0.15*VerseLockDensity"
    )
    bool_lines.append("Deterministic Ranking (Overall):")
    bool_lines.append(
        "Scoring formula: "
        "0.60*GlobalPassRate + 0.25*SuraLockDensity + 0.15*VerseLockDensity"
    )

    for idx, (name, score, global_pass, sura_locks, verse_locks) in enumerate(ranking_rows, start=1):
        lines.append(
            f"{idx}. {name} | score={score:.6f} | global={global_pass}/43 "
            f"| suraLocks={sura_locks} | verseLocks={verse_locks}"
        )
        bool_lines.append(
            f"{idx}. {name} | allGlobalPassed={global_pass == 43} "
            f"| hasSuraLocks={sura_locks > 0} | hasVerseLocks={verse_locks > 0}"
        )

    lines.append("")
    lines.append("Interpretive View:")
    lines.append(f"- Standout: {ranking_rows[0][0]}")
    lines.append(f"- Weakest: {ranking_rows[-1][0]}")
    bool_lines.append("")
    bool_lines.append("Interpretive View:")
    bool_lines.append(f"- Standout: {ranking_rows[0][0]}")
    bool_lines.append(f"- Weakest: {ranking_rows[-1][0]}")

    ranking_json = [
        {
            "rank": idx,
            "mushaf": name,
            "score": round(score, 6),
            "global_pass": global_pass,
            "global_total": 43,
            "all_global_passed": global_pass == 43,
            "sura_locks": sura_locks,
            "has_sura_locks": sura_locks > 0,
            "verse_locks": verse_locks,
            "has_verse_locks": verse_locks > 0,
        }
        for idx, (name, score, global_pass, sura_locks, verse_locks) in enumerate(ranking_rows, start=1)
    ]

    json_payload = {
        "schema_version": "1.2",
        "summary_type": "combined_locks",
        "generated": generated_at,
        "weights": {
            "global": WEIGHT_GLOBAL,
            "sura": WEIGHT_SURA,
            "verse": WEIGHT_VERSE,
        },
        "scoring_formula": "0.60*GlobalPassRate + 0.25*SuraLockDensity + 0.15*VerseLockDensity",
        "datasets": dataset_rows,
        "ranking": ranking_json,
        "interpretive_view": {
            "standout": ranking_rows[0][0],
            "weakest": ranking_rows[-1][0],
        },
    }

    output_path.parent.mkdir(parents=True, exist_ok=True)
    output_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
    bool_output = output_path.parent / (output_path.stem + "_bool.txt")
    bool_output.write_text("\n".join(bool_lines) + "\n", encoding="utf-8")
    json_dir = output_path.parent.parent / "json"
    json_dir.mkdir(parents=True, exist_ok=True)
    with (json_dir / (output_path.stem + ".json")).open("w", encoding="utf-8") as handle:
        json.dump(json_payload, handle, ensure_ascii=True, indent=2)

    _write_per_dataset_grand_summaries(root, generated_at, dataset_rows)


def main() -> None:
    root = Path(__file__).resolve().parent
    output_path = root / "summary" / "txt" / "LOCKS.txt"
    build_locks_summary(root, output_path)
    print(f"Summary written to: {output_path}")


if __name__ == "__main__":
    main()
