import Foundation
import Testing
@testable import Workouts

/// Locks the `info.md` parsing contract: the bundled reference pages must parse into
/// the summary / metadata / sections shape the library detail screen renders, and
/// every shipped motion rig must come with one (the authoring convention).
struct ExerciseInfoTests {

    @Test func bundledChestPressInfoParses() throws {
        let info = try #require(ExerciseInfoLibrary.info(for: "Chest Press"))
        #expect(info.summary.contains("machine bench press"))
        #expect(info.category == "Main circuit")
        #expect(info.type == "Machine-based horizontal press")
        #expect(info.isMachineBased)
        #expect(info.targets == ["Pectorals", "anterior deltoids", "triceps"])
        #expect(info.sections.map(\.title) == ["Setup", "Execution", "Cues", "Common Mistakes", "Progression"])

        let execution = try #require(info.sections.first { $0.title == "Execution" })
        #expect(execution.items.count == 3)
        guard case .step(let firstStep) = execution.items[0] else {
            Issue.record("expected Execution to start with a numbered step")
            return
        }
        // Hard-wrapped source lines must rejoin into one step.
        #expect(firstStep.contains("stop short of locking the elbows"))

        let cues = try #require(info.sections.first { $0.title == "Cues" })
        guard case .bullet = cues.items[0] else {
            Issue.record("expected Cues to be bullets")
            return
        }
    }

    /// The spoken script (fed to `AVSpeechSynthesizer`) leads with the exercise name and
    /// its summary, and `brief` drops the reference-only sections so a hands-free workout
    /// cue reads Setup/Execution/Cues but not Common Mistakes/Progression.
    @Test func spokenScriptBriefDropsReferenceSections() throws {
        let info = try #require(ExerciseInfoLibrary.info(for: "Chest Press"))

        let full = info.spokenScript(name: "Chest Press", brief: false)
        #expect(full.hasPrefix("Chest Press."))
        #expect(full.contains("Setup."))
        #expect(full.contains("Execution."))
        #expect(full.contains("Cues."))
        #expect(full.contains("Common Mistakes."))
        #expect(full.contains("Progression."))

        let brief = info.spokenScript(name: "Chest Press", brief: true)
        #expect(brief.hasPrefix("Chest Press."))
        #expect(brief.contains("Setup."))
        #expect(brief.contains("Execution."))
        #expect(brief.contains("Cues."))
        #expect(!brief.contains("Common Mistakes."))
        #expect(!brief.contains("Progression."))
    }

    /// Every bundled motion rig ships a parseable info page — the exporter copies
    /// `info.md` per entry, and the detail screen counts on the shape.
    @Test func everyBundledExerciseHasParseableInfo() throws {
        #expect(!ExerciseMotionLibrary.exerciseNames.isEmpty)
        for name in ExerciseMotionLibrary.exerciseNames {
            let info = try #require(ExerciseInfoLibrary.info(for: name), "\(name) has no bundled info.md")
            #expect(!info.summary.isEmpty, "\(name): empty summary")
            #expect(!info.targets.isEmpty, "\(name): no targets")
            #expect(!info.sections.isEmpty, "\(name): no sections")
        }
    }
}
