"""Export a separate three-pulley study from measured or flagged photo centers.

Run with the same CadQuery environment used by build_engine.py. This never
changes the public engine or claims that its pump/alternator mounts are known.
"""

from __future__ import annotations

import argparse
import json
import math
from pathlib import Path

import cadquery as cq

import build_engine as engine
from owner_measurements import load_front_drive_layout


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--measurements", type=Path, required=True, help="Measurement JSON exported by the CAD inspector or front-drive photo survey")
    parser.add_argument("--output-dir", type=Path, required=True, help="Directory for the isolated STEP and web geometry")
    parser.add_argument("--allow-photo-estimate", action="store_true", help="Explicitly allow a photo-scaled layout; output remains an estimate, not a direct owner measurement")
    args = parser.parse_args()
    survey = load_front_drive_layout(args.measurements, allow_photo_estimate=args.allow_photo_estimate)
    cp = survey["distances_mm"]["crankToPumpCenter"]
    crank_y = -engine.P["deck_to_crank_axis"]
    pump_y = crank_y + cp
    alt_x = survey["alternator_relative_x_mm"]
    alt_y = crank_y + survey["alternator_relative_y_mm"]
    engine.P.update(survey["direct_diameters_mm"])
    engine.P["water_pump_center_y"] = pump_y
    engine.P["alternator_center_x"] = alt_x
    engine.P["alternator_pivot_y"] = alt_y - engine.P["alternator_shaft_to_pivot_distance"]

    diameters = (engine.P["crank_pulley_outer_diameter"],
                 engine.P["water_pump_sheave_outer_diameter"],
                 engine.P["alternator_pulley_outer_diameter"])
    distances = (cp, survey["distances_mm"]["crankToAlternatorCenter"],
                 survey["distances_mm"]["pumpToAlternatorCenter"])
    for distance, first, second in ((distances[0], 0, 1), (distances[1], 0, 2), (distances[2], 1, 2)):
        if distance <= (diameters[first] + diameters[second]) / 2 + 5:
            raise ValueError("Measured shaft centers would make two pulley envelopes overlap. Recheck the distances and outside diameters.")

    engine.BELT_OUTER_POINTS = engine.belt_outline(5)
    if not all(math.isfinite(value) for point in engine.BELT_OUTER_POINTS for value in point):
        raise ValueError("The derived belt path is invalid; recheck the shaft centers.")
    assembly = cq.Assembly(name="Midget_1500_owner_front_drive_survey")
    parts = (
        ("crank_pulley", engine.make_crank_pulley(), (.24, .25, .23)),
        ("water_pump_sheave", engine.make_water_pump_sheave(), (.27, .28, .26)),
        ("alternator_pulley", engine.make_alternator_pulley(), (.30, .31, .29)),
        ("comparison_belt", engine.make_fan_belt(), (.10, .11, .10)),
    )
    for name, shape, color in parts:
        assembly.add(shape, name=name, color=cq.Color(*color))
    args.output_dir.mkdir(parents=True, exist_ok=True)
    step_path = args.output_dir / "midget-1500-owner-front-drive.step"
    assembly.save(str(step_path))
    step_path.write_text("\n".join(line.rstrip() for line in step_path.read_text().splitlines()) + "\n")
    web_parts = []
    for name, shape, color in parts:
        vertices, triangles = shape.tessellate(.7, .16)
        web_parts.append({"name": name,
                          "vertices": [round(c, 3) for vertex in vertices for c in vertex.toTuple()],
                          "triangles": [index for face in triangles for index in face],
                          "color": list(color)})
    output = {
        "units": "mm", "parts": web_parts,
        "checks": {
            "shaft_centers_mm": {"crank": [0, crank_y], "water_pump": [0, pump_y],
                                 "alternator": [alt_x, alt_y]},
            "recorded_distances_mm": survey["distances_mm"],
            "outside_pulley_diameters_mm": {
                "crank": diameters[0], "water_pump": diameters[1], "alternator": diameters[2]},
            "comparison_belt_outer_outline_mm": round(engine.polygon_perimeter(engine.BELT_OUTER_POINTS), 1),
            "belt_lane_center_z_mm": engine.P["belt_lane_z"],
            "limits": "Three center distances define a triangle, not its orientation relative to the engine. Photo-scaled distances are estimates affected by perspective and scale placement. This study assumes the water-pump shaft is directly above the crank and the alternator is on the positive-X side. Unmeasured pulley diameters, every groove profile, axial alignment and belt cross-section remain model comparisons. No brackets, pump housing, fan or vehicle body are positioned by this survey. Do not use the outline as a belt part number or removal clearance."},
        "provenance": survey,
    }
    (args.output_dir / "midget-1500-owner-front-drive.json").write_text(json.dumps(output, separators=(",", ":")))
    print(f"Wrote {step_path}")
    print(f"Comparison belt outer outline: {output['checks']['comparison_belt_outer_outline_mm']} mm")


if __name__ == "__main__":
    main()
