#!/usr/bin/env python3
"""Synthetic end-to-end test (no real NeuroVault data): builds a fake bundle root with simulated group T/Z maps whose
whole-brain rms effect follows delta_rms = 0.25*(N/30)^beta, so analyze.py should recover beta on log N.
Usage: make_synthetic.py <root> <mask.nii.gz> <beta> <params_template>"""
import csv, hashlib, json, os, shutil, sys
import numpy as np, nibabel as nib
from scipy import ndimage
root, maskp, beta, tmpl = sys.argv[1], sys.argv[2], float(sys.argv[3]), sys.argv[4]
rng = np.random.default_rng(1)
os.makedirs(f"{root}/data/maps", exist_ok=True); os.makedirs(f"{root}/plan", exist_ok=True)
shutil.copy(maskp, f"{root}/data/mask_gm_2mm.nii.gz")
m = nib.load(maskp); M = np.asarray(m.dataobj) > 0
pattern = ndimage.gaussian_filter(rng.standard_normal(M.shape), 3); pattern /= pattern[M].std()
rows, pins = [], []
iid = 1000
tasks = ["task A", "task B", "unknown"]
for c in range(60):
    N = int(np.exp(rng.uniform(np.log(10), np.log(400))))
    task = tasks[c % 3]
    for k in range(rng.integers(1, 5)):
        iid += 1
        mt = "T" if rng.random() < 0.6 else "Z"
        drms = 0.25 * (N / 30) ** beta * np.exp(rng.normal(0, 0.15))
        lam = drms * np.sqrt(N) * pattern
        noise = ndimage.gaussian_filter(rng.standard_normal(M.shape), 1.5); noise /= noise.std()
        if mt == "T":
            nu = N - 1
            chi = rng.chisquare(nu, M.shape) / nu
            v = (noise + lam) / np.sqrt(chi)
        else:
            v = noise + lam
        v = np.where(M, v, 0).astype(np.float32)
        if iid == 1003: v = np.where(M, v / 50, 0).astype(np.float32)   # a mislabelled beta-like map -> X5
        p = f"{root}/data/maps/{iid}.nii.gz"; nib.Nifti1Image(v, m.affine).to_filename(p)
        if iid == 1005: shutil.copy(f"{root}/data/maps/1004.nii.gz", p)  # duplicate content -> X7
        pins.append({"image_id": iid, "sha256": hashlib.sha256(open(p, "rb").read()).hexdigest(), "bytes": os.path.getsize(p), "status": "ok"})
        rows.append({"image_id": iid, "collection_id": c + 1, "file_url": "synthetic", "N": N, "map_type": mt, "df_meta": "",
                     "fwhm_mm": "" if c % 4 == 0 else "6", "upload_year": str(2016 + c % 8), "task": task, "task_id": "",
                     "doi": "synthetic", "name": "a vs rest" if k == 0 else "a vs b", "contrast_definition": ""})
pins.append({"image_id": 99999, "sha256": "", "bytes": "", "status": "download_failed"})
rows.append({**rows[0], "image_id": 99999})
for path, data in ((f"{root}/data/included_maps.csv", rows), (f"{root}/data/map_pins.csv", pins)):
    with open(path, "w", newline="") as fh:
        w = csv.DictWriter(fh, fieldnames=list(data[0].keys()), lineterminator="\n"); w.writeheader(); w.writerows(data)
s = open(tmpl).read().replace("__FROZEN__", hashlib.sha256(open(f"{root}/data/included_maps.csv", "rb").read()).hexdigest()).replace("__REPORTBY__", "2026-10-22")
p = json.loads(s); p["roi_min_collections"] = 10; p["one_per_collection_draws"] = 20
json.dump(p, open(f"{root}/plan/parameters.json", "w"), indent=1)
print("maps", len(rows))
