import json

import FreeCAD as App
import Part
import Sketcher

from freecad_mutation_evidence import capture_property_mutation


def version_text():
    return ".".join(str(value) for value in App.Version()[:3])


def square_sketch(body, name, half_size, z=0.0):
    sketch = body.newObject("Sketcher::SketchObject", name)
    points = [(-half_size, -half_size), (half_size, -half_size), (half_size, half_size), (-half_size, half_size)]
    for index, start in enumerate(points):
        end = points[(index + 1) % len(points)]
        sketch.addGeometry(Part.LineSegment(App.Vector(start[0], start[1], 0), App.Vector(end[0], end[1], 0)), False)
    sketch.Placement.Base.z = z
    return sketch


def shape_result(case_id, document, feature):
    document.recompute()
    shape = feature.Shape
    box = shape.BoundBox if not shape.isNull() else None
    return {
        "id": case_id,
        "passed": not shape.isNull() and shape.isValid(),
        "status": feature.getStatusString(),
        "solids": len(shape.Solids) if not shape.isNull() else 0,
        "faces": len(shape.Faces) if not shape.isNull() else 0,
        "volume": float(shape.Volume) if not shape.isNull() else 0.0,
        "bounds": None if box is None else {
            "min": [float(box.XMin), float(box.YMin), float(box.ZMin)],
            "max": [float(box.XMax), float(box.YMax), float(box.ZMax)],
        },
    }


def tapered_pad():
    document = App.newDocument("TaperedPadOracle")
    body = document.addObject("PartDesign::Body", "Body")
    sketch = square_sketch(body, "Sketch", 2.0)
    pad = body.newObject("PartDesign::Pad", "Pad")
    pad.Profile = sketch
    pad.Type = "Length"
    pad.Length = 5.0
    pad.TaperAngle = 5.0
    mutation = capture_property_mutation(document, pad, "Length", 6.0)
    result = shape_result("pad-tapered", document, pad)
    result["mutation"] = mutation
    App.closeDocument(document.Name)
    return result


def base_and_pocket_document(name, half_size=2.0):
    document = App.newDocument(name)
    body = document.addObject("PartDesign::Body", "Body")
    base = body.newObject("PartDesign::Feature", "Base")
    base.Shape = Part.makeBox(10, 10, 10, App.Vector(-5, -5, -5))
    sketch = square_sketch(body, "Sketch", half_size, 5.0)
    pocket = body.newObject("PartDesign::Pocket", "Pocket")
    pocket.Profile = sketch
    if hasattr(pocket, "BaseFeature"):
        pocket.BaseFeature = base
    return document, body, base, sketch, pocket


def tapered_pocket():
    document, _, _, _, pocket = base_and_pocket_document("TaperedPocketOracle")
    pocket.Type = "Length"
    pocket.Length = 10.0
    pocket.TaperAngle = 2.0
    mutation = capture_property_mutation(document, pocket, "Length", 8.0)
    result = shape_result("pocket-tapered", document, pocket)
    result["mutation"] = mutation
    App.closeDocument(document.Name)
    return result


def through_all_pocket():
    document, _, _, _, pocket = base_and_pocket_document("ThroughAllPocketOracle", 1.0)
    pocket.Type = 1
    result = shape_result("pocket-through-all", document, pocket)
    App.closeDocument(document.Name)
    return result


def up_to_face_pocket():
    document, _, base, _, pocket = base_and_pocket_document("UpToFacePocketOracle", 1.0)
    document.recompute()
    top_face = min(range(len(base.Shape.Faces)), key=lambda index: base.Shape.Faces[index].CenterOfMass.z) + 1
    pocket.Type = 3
    pocket.UpToFace = (base, ["Face{}".format(top_face)])
    result = shape_result("pocket-up-to-face", document, pocket)
    result["targetFace"] = top_face
    App.closeDocument(document.Name)
    return result


def midplane_pad():
    document = App.newDocument("MidplanePadOracle")
    body = document.addObject("PartDesign::Body", "Body")
    sketch = square_sketch(body, "Sketch", 2.0)
    pad = body.newObject("PartDesign::Pad", "Pad")
    pad.Profile = sketch
    pad.Type = "Length"
    pad.Length = 6.0
    pad.Midplane = True
    result = shape_result("pad-midplane", document, pad)
    App.closeDocument(document.Name)
    return result


cases = [tapered_pad(), tapered_pocket(), through_all_pocket(), up_to_face_pocket(), midplane_pad()]
version = App.Version()
report = {
    "schemaVersion": 1,
    "baselineId": "freecad-1.1.1-partdesign-base-oracle",
    "freecadVersion": version_text(),
    "gitCommit": "0108fd4b4850cc46e625b60e53cea7a7bbe69f8d",
    "status": "pass" if all(case["passed"] for case in cases) else "failed",
    "tolerance": 1e-7,
    "cases": cases,
    "summary": {"cases": len(cases), "passed": sum(1 for case in cases if case["passed"])},
}
print("FREECAD_PARTDESIGN_BASE_RESULT=" + json.dumps(report, sort_keys=True, separators=(",", ":")))
