#!/usr/bin/env python3
"""Scalar actuator screening, not FEA, gait or thermal validation.

tau_joint = (total_mass * gravity * vertical_load_multiplier / stance_feet) * r
tau_actuator_output = tau_joint / (external_ratio * external_efficiency)

Published torques already include the internal gearbox; do not multiply them by
internal_ratio again. r is perpendicular to the force line. Rated running torque
is not necessarily continuous stall torque; neither is proven in a printed mount.
"""
import csv
import hashlib
import itertools
import json
from pathlib import Path

ROOT = Path(__file__).resolve().parent


def main():
    inputs = {name: (ROOT / name).read_bytes()
              for name in ("requirements.json", "motor_candidates.json")}
    requirements = json.loads(inputs["requirements.json"])
    database = json.loads(inputs["motor_candidates.json"])
    assumptions = requirements["screening_assumptions"]
    count = requirements["proposed_architecture"]["actuator_count"]
    motors = {item["id"]: item for item in database["candidates"]}
    if len(motors) != len(database["candidates"]):
        raise ValueError("Duplicate motor id")
    for motor in motors.values():
        if not 0 < motor["rated_torque_nm"] <= motor["peak_torque_nm"]:
            raise ValueError("Invalid torque data: " + motor["id"])
    if not 0 < assumptions["external_transmission_efficiency"] <= 1:
        raise ValueError("Invalid external efficiency")
    if assumptions["external_transmission_ratio"] <= 0 or assumptions["supporting_feet"] <= 0:
        raise ValueError("Invalid load or transmission input")
    rows = []
    for mass, arm, multiplier, motor in itertools.product(
        assumptions["total_mass_kg"], assumptions["perpendicular_moment_arm_m"],
        assumptions["vertical_load_multiplier"], motors.values()
    ):
        force = mass * assumptions["gravity_m_s2"] * multiplier / assumptions["supporting_feet"]
        joint_torque = force * arm
        output_torque = joint_torque / (assumptions["external_transmission_ratio"]
                                        * assumptions["external_transmission_efficiency"])
        stall = motor["rated_stall_torque_nm"]
        rows.append({
            "motor": motor["id"], "total_mass_kg": mass, "moment_arm_m": arm,
            "force_multiplier": multiplier, "vertical_force_per_foot_n": round(force, 6),
            "joint_torque_nm": round(joint_torque, 6),
            "actuator_output_torque_nm": round(output_torque, 6),
            "demand_divided_by_catalog_running_rated_torque": round(output_torque / motor["rated_torque_nm"], 6),
            "demand_divided_by_catalog_stall_rated_torque": round(output_torque / stall, 6) if stall else "",
            "demand_divided_by_catalog_peak_torque": round(output_torque / motor["peak_torque_nm"], 6),
        })
    configurations = [
        {"id": "uniform_" + mid, "counts": {mid: count}, "status": "candidate only"}
        for mid in motors
    ] + database["mixed_configurations"]
    budgets = []
    for config in configurations:
        if sum(config["counts"].values()) != count:
            raise ValueError("Incorrect actuator count: " + config["id"])
        motor_mass = sum(motors[mid]["mass_g"] * n for mid, n in config["counts"].items()) / 1000
        price_totals = {}
        for mid, n in config["counts"].items():
            price = motors[mid]["unit_reference_price"]
            currency = price["currency"]
            price_totals[currency] = round(price_totals.get(currency, 0) + price["amount"] * n, 2)
        budgets.append({
            "id": config["id"], "counts": config["counts"],
            "status": config["status"], "motor_mass_kg": round(motor_mass, 3),
            "published_reference_price_totals": price_totals,
            "remaining_mass_for_everything_else_kg": {
                str(m): round(m - motor_mass, 3) for m in assumptions["total_mass_kg"]
            }
        })
    output = ROOT / "screening"
    output.mkdir(exist_ok=True)
    with (output / "torque_sensitivity.csv").open("w", newline="") as f:
        writer = csv.DictWriter(f, fieldnames=list(rows[0]))
        writer.writeheader()
        writer.writerows(rows)
    summary = {
        "method": "scalar equal vertical load sharing; not gait or thermal simulation",
        "case_count": len(rows),
        "input_sha256": {name: hashlib.sha256(data).hexdigest() for name, data in inputs.items()},
        "script_sha256": hashlib.sha256(Path(__file__).read_bytes()).hexdigest(),
        "limitations": assumptions["notes"],
        "price_note": database["price_basis"],
        "configurations": budgets
    }
    (output / "summary.json").write_text(json.dumps(summary, ensure_ascii=False, indent=2) + "\n")
    print(json.dumps({"cases": len(rows), "configurations": len(budgets),
                      "output": str(output)}, ensure_ascii=False))


if __name__ == "__main__":
    main()
