"""ALE-style evaluator for `dde_period_doubling_point_1`.

evaluate(task_dir, output_dir) -> dict with score in [0, 1]. Fully
self-certifying: the bifurcation condition mu = -1 is recomputed from the
submitted (tau*, orbit) pair; no hidden reference value exists.
Requires bif_common.py alongside this file.
"""

import json
import os
import sys

import numpy as np

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from bif_common import (grade_bifurcation, grade_doubled,  # noqa: E402
                        verify_bifurcation, verify_doubled)

CLAIM_FACTOR = 10.0


def evaluate(task_dir: str, output_dir: str) -> dict:
    log = {"score": 0.0}
    try:
        sol = np.load(os.path.join(output_dir, "solution.npz"))
        with open(os.path.join(output_dir, "results.json")) as fh:
            claimed = json.load(fh)
        m = verify_bifurcation(sol)
        log["metrics"] = m
    except Exception as exc:
        log["reason"] = f"invalid submission: {exc}"
        return log

    if m.get("structurally_valid"):
        try:
            c_tau = float(claimed["tau_star"])
            c_res = float(claimed["resid_linf"])
        except Exception:
            log["reason"] = "results.json missing tau_star / resid_linf"
            return log
        if abs(c_tau - m["tau_star"]) > 1e-9 * max(1.0, abs(m["tau_star"])):
            log["reason"] = (f"results.json tau_star {c_tau} disagrees with "
                             f"solution.npz {m['tau_star']}")
            return log
        r = m["resid_linf"]
        if not (r / CLAIM_FACTOR <= max(c_res, 1e-16)
                <= max(r * CLAIM_FACTOR, 1e-13)):
            log["reason"] = (f"untruthful resid_linf: claimed {c_res:g}, "
                             f"recomputed {r:g}")
            return log

    score, reason = grade_bifurcation(m)
    if score == 0.0:
        log.update(score=0.0, reason="bifurcation point: " + reason)
        return log

    m2 = verify_doubled(sol)
    log["metrics_doubled"] = m2
    ok, reason2 = grade_doubled(m2)
    if not ok:
        # the bifurcation point is right but the branch switch is not
        log.update(score=0.5, reason="branch switch: " + reason2)
        return log
    log.update(score=score, reason=f"{reason}; {reason2}")
    return log


if __name__ == "__main__":
    print(json.dumps(evaluate(sys.argv[1], sys.argv[2]), indent=2, default=float))
