import json
import math

import FreeCAD as App
import Part
import Sketcher
import TestSketcherApp

from freecad_mutation_evidence import capture_property_mutation


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


def shape_result(case_id, document, feature):
    document.recompute()
    shape = feature.Shape
    return {
        "id": case_id,
        "typeId": feature.TypeId,
        "passed": not shape.isNull() and shape.isValid() and len(shape.Solids) == 1,
        "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,
    }


def additive_loft():
    document = App.newDocument("AdditiveLoftOracle")
    body = document.addObject("PartDesign::Body", "Body")
    profile = body.newObject("Sketcher::SketchObject", "Profile")
    TestSketcherApp.CreateRectangleSketch(profile, (0, 0), (1, 1))
    section = body.newObject("Sketcher::SketchObject", "Section")
    section.MapMode = "FlatFace"
    section.AttachmentSupport = (document.XZ_Plane, [""])
    document.recompute()
    TestSketcherApp.CreateRectangleSketch(section, (0, 1), (1, 1))
    feature = body.newObject("PartDesign::AdditiveLoft", "AdditiveLoft")
    feature.Profile = profile
    feature.Sections = [section]
    mutation = capture_property_mutation(document, feature, "Ruled", True)
    result = shape_result("additive-loft", document, feature)
    result["mutation"] = mutation
    App.closeDocument(document.Name)
    return result


def subtractive_loft():
    document = App.newDocument("SubtractiveLoftOracle")
    body = document.addObject("PartDesign::Body", "Body")
    pad_sketch = body.newObject("Sketcher::SketchObject", "PadSketch")
    TestSketcherApp.CreateRectangleSketch(pad_sketch, (0, 0), (1, 1))
    pad = body.newObject("PartDesign::Pad", "Pad")
    pad.Profile = pad_sketch
    pad.Length = 2
    document.recompute()
    profile = body.newObject("Sketcher::SketchObject", "Profile")
    TestSketcherApp.CreateRectangleSketch(profile, (0, 0), (1, 1))
    section = body.newObject("Sketcher::SketchObject", "Section")
    section.MapMode = "FlatFace"
    section.AttachmentSupport = (document.XZ_Plane, [""])
    document.recompute()
    TestSketcherApp.CreateRectangleSketch(section, (0, 1), (1, 1))
    edited_section = body.newObject("Sketcher::SketchObject", "EditedSection")
    edited_section.MapMode = "FlatFace"
    edited_section.AttachmentSupport = (document.XZ_Plane, [""])
    document.recompute()
    TestSketcherApp.CreateRectangleSketch(edited_section, (0, 1), (0.75, 0.75))
    feature = body.newObject("PartDesign::SubtractiveLoft", "SubtractiveLoft")
    feature.Profile = profile
    feature.Sections = [section]
    mutation = capture_property_mutation(document, feature, "Sections", [edited_section])
    result = shape_result("subtractive-loft", document, feature)
    result["mutation"] = mutation
    App.closeDocument(document.Name)
    return result


def pipe_sketches(document, body):
    profile = body.newObject("Sketcher::SketchObject", "Profile")
    TestSketcherApp.CreateCircleSketch(profile, (0, 0), 1)
    edited_profile = body.newObject("Sketcher::SketchObject", "EditedProfile")
    TestSketcherApp.CreateCircleSketch(edited_profile, (0, 0), 0.75)
    spine = body.newObject("Sketcher::SketchObject", "Spine")
    spine.MapMode = "FlatFace"
    spine.AttachmentSupport = (document.XZ_Plane, [""])
    document.recompute()
    spine.addGeometry(Part.LineSegment(App.Vector(0, 0, 0), App.Vector(0, 1, 0)), False)
    spine.addConstraint(Sketcher.Constraint("Coincident", 0, 1, -1, 1))
    spine.addConstraint(Sketcher.Constraint("PointOnObject", 0, 2, -2))
    spine.addConstraint(Sketcher.Constraint("DistanceY", 0, 1, 0, 2, 1))
    return profile, edited_profile, spine


def additive_pipe():
    document = App.newDocument("AdditivePipeOracle")
    body = document.addObject("PartDesign::Body", "Body")
    profile, edited_profile, spine = pipe_sketches(document, body)
    feature = body.newObject("PartDesign::AdditivePipe", "AdditivePipe")
    feature.Profile = profile
    feature.Spine = spine
    mutation = capture_property_mutation(document, feature, "Profile", edited_profile)
    result = shape_result("additive-pipe", document, feature)
    result["mutation"] = mutation
    result["transition"] = str(feature.Transition)
    App.closeDocument(document.Name)
    return result


def subtractive_pipe():
    document = App.newDocument("SubtractivePipeOracle")
    body = document.addObject("PartDesign::Body", "Body")
    profile, edited_profile, spine = pipe_sketches(document, body)
    pad_sketch = body.newObject("Sketcher::SketchObject", "PadSketch")
    TestSketcherApp.CreateRectangleSketch(pad_sketch, (-5, -5), (10, 10))
    pad = body.newObject("PartDesign::Pad", "Pad")
    pad.Profile = pad_sketch
    pad.Length = 1
    document.recompute()
    feature = body.newObject("PartDesign::SubtractivePipe", "SubtractivePipe")
    feature.Profile = profile
    feature.Spine = spine
    mutation = capture_property_mutation(document, feature, "Profile", edited_profile)
    result = shape_result("subtractive-pipe", document, feature)
    result["mutation"] = mutation
    result["transition"] = str(feature.Transition)
    App.closeDocument(document.Name)
    return result


cases = [additive_loft(), subtractive_loft(), additive_pipe(), subtractive_pipe()]
report = {
    "schemaVersion": 1,
    "baselineId": "freecad-1.1.1-partdesign-loft-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_LOFT_RESULT=" + json.dumps(report, sort_keys=True, separators=(",", ":")))
