import Foundation
import Testing
@testable import Workouts

/// Locks `ExerciseCatalog`'s section grouping and search — the curated,
/// category-ordered view over the bundled exercise library. Every
/// `ExerciseMotionLibrary.exerciseNames` entry must land in a real authored category,
/// not the "Other" catch-all: a fall into "Other" here would mean a motion rig shipped
/// without a matching `info.md` category — a real data gap worth surfacing, not
/// something to silently bucket away.
struct ExerciseCatalogTests {

    @Test func everyBundledExerciseHasACuratedSection() {
        let otherSection = ExerciseCatalog.sections.first { $0.title == ExerciseCatalog.otherTitle }
        let uncategorized = otherSection?.names ?? []
        #expect(uncategorized.isEmpty,
                "expected every bundled exercise to have a curated category; found in Other: \(uncategorized)")

        let allNames = Set(ExerciseCatalog.sections.flatMap(\.names))
        #expect(allNames == Set(ExerciseMotionLibrary.exerciseNames))
    }

    @Test func sectionsCoverEveryNameExactlyOnce() {
        let allNames = ExerciseCatalog.sections.flatMap(\.names)
        #expect(allNames.count == Set(allNames).count, "an exercise appeared in more than one section")
    }

    @Test func namesAreAlphabeticalWithinASection() {
        for section in ExerciseCatalog.sections {
            #expect(section.names == section.names.sorted(), "\(section.title) is not alphabetized")
        }
    }

    @Test func emptyQueryReturnsEverySection() {
        let all = ExerciseCatalog.sections(matching: "")
        #expect(all.flatMap(\.names).count == ExerciseCatalog.sections.flatMap(\.names).count)

        let whitespaceOnly = ExerciseCatalog.sections(matching: "   ")
        #expect(whitespaceOnly.flatMap(\.names).count == ExerciseCatalog.sections.flatMap(\.names).count)
    }

    @Test func queryMatchesByExerciseName() {
        let results = ExerciseCatalog.sections(matching: "Chest Press")
        let names = results.flatMap(\.names)
        #expect(names.contains("Chest Press"))
        #expect(names.allSatisfy { $0.localizedCaseInsensitiveContains("Chest Press") })
    }

    @Test func queryMatchesByCategory() {
        let results = ExerciseCatalog.sections(matching: "stretching")
        let names = Set(results.flatMap(\.names))
        #expect(!names.isEmpty)
        for name in names {
            let info = ExerciseInfoLibrary.info(for: name)
            #expect(info?.category?.localizedCaseInsensitiveContains("stretching") == true)
        }
    }

    @Test func queryMatchesByTarget() throws {
        let info = try #require(ExerciseInfoLibrary.info(for: "Chest Press"))
        let target = try #require(info.targets.first)
        let results = ExerciseCatalog.sections(matching: target)
        #expect(results.flatMap(\.names).contains("Chest Press"))
    }

    @Test func queryDropsSectionsWithNoMatches() {
        let results = ExerciseCatalog.sections(matching: "zzz-not-a-real-exercise-zzz")
        #expect(results.isEmpty)
    }
}
