import XCTest
@testable import PoseCore

final class LeanAnalysisTests: XCTestCase {
    let panel = PixelRect(x: 0, y: 0, width: 1000, height: 1000)
    func line(_ x: Double, _ y: Double) -> LeanLine {
        LeanLine(a: Point2(x: 400, y: 400), b: Point2(x: 400 + x, y: 400 + y), uncertainty: 1)
    }
    func testUndirectedAnglesAndInvalidSegments() throws {
        for (x, y, expected) in [(0.0, 100.0, 0.0), (100, 100, 45), (100, 0, 90)] {
            let l = line(x, y)
            XCTAssertEqual(try XCTUnwrap(LeanAnalysis.magnitude(l, reference: nil, panel: panel)).angle, expected, accuracy: 1e-8)
            let reversed = LeanLine(a: l.b, b: l.a, uncertainty: l.uncertainty)
            XCTAssertEqual(LeanAnalysis.magnitude(l, reference: nil, panel: panel), LeanAnalysis.magnitude(reversed, reference: nil, panel: panel))
        }
        for bad in [line(0, 0), line(1, 0), line(.nan, 0), line(1000, 0)] {
            XCTAssertNil(LeanAnalysis.magnitude(bad, reference: nil, panel: panel))
        }
        var bad = line(0, 100); bad.uncertainty = 0
        XCTAssertNil(LeanAnalysis.magnitude(bad, reference: nil, panel: panel))
    }
    func testSensitivityFoldsAtUprightAndHorizontal() throws {
        let upright = try XCTUnwrap(LeanAnalysis.magnitude(line(0, 100), reference: nil, panel: panel))
        XCTAssertEqual(upright.lower, 0); XCTAssertGreaterThan(upright.upper, 1)
        let horizontal = try XCTUnwrap(LeanAnalysis.magnitude(line(100, 0), reference: nil, panel: panel))
        XCTAssertEqual(horizontal.upper, 90); XCTAssertLessThan(horizontal.lower, 89)
        let base = line(100, 100)
        let measured = try XCTUnwrap(LeanAnalysis.magnitude(base, reference: nil, panel: panel))
        for t in stride(from: 0.0, to: 2 * Double.pi, by: 0.2) {
            var perturbed = base
            perturbed.a.x += cos(t); perturbed.a.y += sin(t)
            perturbed.b.x -= cos(t); perturbed.b.y -= sin(t)
            let value = try XCTUnwrap(LeanAnalysis.magnitude(perturbed, reference: nil, panel: panel)).angle
            XCTAssertTrue((measured.lower...measured.upper).contains(value))
        }
    }
    func testSharedReferenceCancelsRollAndReversal() throws {
        let wheel = line(60, -100), background = line(100, 30)
        let first = try XCTUnwrap(LeanAnalysis.relative(wheel, reference: background, panel: panel))
        func rotate(_ l: LeanLine) -> LeanLine {
            func p(_ p: Point2) -> Point2 { Point2(x: 800 - p.y, y: p.x) }
            return LeanLine(a: p(l.b), b: p(l.a), uncertainty: l.uncertainty)
        }
        let second = try XCTUnwrap(LeanAnalysis.relative(rotate(wheel), reference: rotate(background), panel: panel))
        XCTAssertEqual(first.angle, second.angle, accuracy: 1e-8)
        XCTAssertNil(LeanAnalysis.relative(line(0, 100), reference: line(100, 0), panel: panel), "Near a modulo-180 branch boundary is ambiguous")
    }
}
