import XCTest
@testable import PoseCore

final class ProvisionalCoachingTests: XCTestCase {
    func testExactThresholdsAndComparability() throws {
        let doc = try reviewedPair()
        let pair = try XCTUnwrap(PairReview.resolve(doc.frames).first)
        let original = try PairAnalysis.analyze(doc, pair: pair)
        var context = original.context; context.cornerDirection = .left
        for (delta, count) in [(9.99, 0), (10.0, 1), (-9.99, 0), (-10.0, 1)] {
            let observation = PairObservation(feature: .leftElbow, subject: original.observations[0].subject,
                reference: original.observations[0].reference, delta: delta, comparisonUnavailable: nil)
            let report = PairReport(evidenceHash: original.evidenceHash, pair: pair, revision: 0, context: context,
                analyzerVersion: "test", catalogVersion: "test", observations: [observation], focus: [], activeRuleCount: 0)
            XCTAssertEqual(report.coaching.count, count)
        }
        var changed = doc; changed.comparisonContexts[0].cornerDirection = .left
        changed.comparisonContexts[0].comparability[.leftElbow] = .notComparable
        let subject = try XCTUnwrap(changed.frames.firstIndex { $0.id == pair.subjectID })
        try changed.frames[subject].setJoint("left_wrist", point: Point2(x: 300, y: 200), state: .corrected)
        XCTAssertTrue(try PairAnalysis.analyze(changed, pair: pair).coaching.isEmpty)
    }
    func testElbowRolesThresholdAndEvidenceGates() throws {
        var doc = try reviewedPair()
        let pair = try XCTUnwrap(PairReview.resolve(doc.frames).first)
        doc.comparisonContexts[0].cornerDirection = .left
        let subjectIndex = try XCTUnwrap(doc.frames.firstIndex { $0.id == pair.subjectID })
        try doc.frames[subjectIndex].setJoint("left_wrist", point: Point2(x: 300, y: 200), state: .corrected)
        var report = try PairAnalysis.analyze(doc, pair: pair)
        XCTAssertEqual(report.coaching.first?.title, "Inside elbow: explore more bend")
        doc.comparisonContexts[0].cornerDirection = .right
        report = try PairAnalysis.analyze(doc, pair: pair)
        XCTAssertEqual(report.coaching.first?.title, "Outside elbow: explore more bend")
        doc.comparisonContexts[0].cornerDirection = .unknown
        XCTAssertTrue(try PairAnalysis.analyze(doc, pair: pair).coaching.isEmpty)
        doc.comparisonContexts[0].cornerDirection = .left
        doc.comparisonContexts[0].subjectConfirmed = false
        XCTAssertEqual(try PairAnalysis.analyze(doc, pair: pair).coaching.count, 1)
    }

    func testKneesRequireVisualEvidenceNeverAngleDelta() throws {
        var doc = try reviewedPair()
        let pair = try XCTUnwrap(PairReview.resolve(doc.frames).first)
        doc.comparisonContexts[0].cornerDirection = .left
        try doc.frames[0].setJoint("left_ankle", point: Point2(x: 300, y: 200), state: .corrected)
        XCTAssertTrue(try PairAnalysis.analyze(doc, pair: pair).coaching.isEmpty)
        doc.comparisonContexts[0].kneePlacement = [.leftKnee: .closerToTank, .rightKnee: .fartherFromTank]
        let report = try PairAnalysis.analyze(doc, pair: pair)
        XCTAssertEqual(report.coaching.map(\.title), ["Inside knee: open toward the turn", "Outside knee: hold closer to the tank"])
        XCTAssertTrue(report.coaching.allSatisfy { $0.provenance == "user-assumed-reference" })
        try doc.frames[0].setJoint("left_knee", point: nil, state: .occluded)
        XCTAssertEqual(try PairAnalysis.analyze(doc, pair: pair).coaching.count, 1, "An explicitly obscured knee must suppress its visual advice")
        doc.comparisonContexts[0].kneePlacement = [.leftKnee: .similar, .rightKnee: .unassessable]
        XCTAssertTrue(try PairAnalysis.analyze(doc, pair: pair).coaching.isEmpty)
    }

    func testVisualEvidenceRoundTripAndOldContext() throws {
        var doc = try reviewedPair()
        doc.comparisonContexts[0].kneePlacement = [.leftKnee: .closerToTank]
        let encoder = JSONEncoder()
        let restored = try JSONDecoder().decode(ReviewDocument.self, from: encoder.encode(doc))
        XCTAssertEqual(restored.comparisonContexts, doc.comparisonContexts)
        doc.comparisonContexts[0].kneePlacement = nil
        let old = try JSONDecoder().decode(ReviewDocument.self, from: encoder.encode(doc))
        XCTAssertNil(old.comparisonContexts[0].kneePlacement)
    }
}
