//
// ExerciseCatalog.swift
// Workouts
//
// Copyright 2026 Rouslan Zenetl. All Rights Reserved.
//

import Foundation

/// One group of exercises in the picker, in catalog display order.
struct ExerciseCatalogSection {
    let title: String
    let names: [String]
}

/// Curated, searchable sections over the bundled exercise library — every name from
/// `ExerciseMotionLibrary.exerciseNames`, grouped by `ExerciseInfoLibrary.info(for:)?.category`
/// in a hand-picked category order rather than the raw alphabetical/declaration order.
/// Building the grouping is a `Bundle.main` resource read per exercise, so it's done once
/// as an immutable `static let` (Swift 6 strict concurrency: no shared mutable state).
enum ExerciseCatalog {
    /// Bucket for an exercise with no bundled `info.md`, or one whose category isn't in
    /// `categoryOrder` — always sorts last.
    static let otherTitle = "Other"

    /// Curated display order for the categories actually authored in the library's
    /// `info.md` files (grep `**Category:**` under `Workouts/Resources/ExerciseMotions/`):
    /// "Mobility / warm-up", "Warm-up", "Warm-up and main circuit", "Main circuit",
    /// "Stretching", "Cardio", "Mindfulness". Warm-up/mobility categories lead (that's
    /// how a workout starts), then the main circuit, then stretching/cardio/mindfulness.
    /// Any category found in the data but missing here still gets its own section
    /// (appended before "Other") rather than silently absorbed into it.
    private static let categoryOrder: [String] = [
        "Mobility / warm-up",
        "Warm-up",
        "Warm-up and main circuit",
        "Main circuit",
        "Stretching",
        "Cardio",
        "Mindfulness",
    ]

    /// Every bundled exercise, grouped into curated-order sections with alphabetized
    /// names within each section.
    static let sections: [ExerciseCatalogSection] = {
        var namesByCategory: [String: [String]] = [:]
        for name in ExerciseMotionLibrary.exerciseNames {
            let category = ExerciseInfoLibrary.info(for: name)?.category ?? otherTitle
            namesByCategory[category, default: []].append(name)
        }

        var order = categoryOrder
        for category in namesByCategory.keys where category != otherTitle && !order.contains(category) {
            order.append(category)
        }
        order.append(otherTitle)

        return order.compactMap { category -> ExerciseCatalogSection? in
            guard let names = namesByCategory[category], !names.isEmpty else { return nil }
            return ExerciseCatalogSection(title: category, names: names.sorted())
        }
    }()

    /// `sections` filtered to exercises matching `query` (case-insensitive) against the
    /// exercise name, its category, or any of its `ExerciseInfo.targets` — preserving
    /// section structure and dropping sections left with no matches. An empty/whitespace
    /// query returns every section unfiltered.
    static func sections(matching query: String) -> [ExerciseCatalogSection] {
        let trimmed = query.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !trimmed.isEmpty else { return sections }

        return sections.compactMap { section -> ExerciseCatalogSection? in
            let matches = section.names.filter { name in
                if name.localizedCaseInsensitiveContains(trimmed) { return true }
                guard let info = ExerciseInfoLibrary.info(for: name) else { return false }
                if let category = info.category, category.localizedCaseInsensitiveContains(trimmed) {
                    return true
                }
                return info.targets.contains { $0.localizedCaseInsensitiveContains(trimmed) }
            }
            return matches.isEmpty ? nil : ExerciseCatalogSection(title: section.title, names: matches)
        }
    }
}
