import XCTest
@testable import MixBoard

final class GroupTemplateResolverTests: XCTestCase {

    private func makeTrack(
        artist: String = "Test Artist",
        album: String = "Test Album",
        genre: String = "Electronic",
        filePath: String = "Music/subfolder/song.mp3",
        fileFormat: String = "MP3",
        bpm: Double? = 128.0,
        musicalKey: String? = "Am",
        year: Int? = 2023
    ) -> Track {
        let track = Track(title: "Test Song", artist: artist, album: album, genre: genre, filePath: filePath, fileFormat: fileFormat)
        track.bpm = bpm
        track.musicalKey = musicalKey
        track.year = year
        return track
    }

    // MARK: - Basic Placeholder Resolution

    func testResolveArtistTemplate() {
        let track = makeTrack(artist: "Raekwon")
        let result = GroupTemplateResolver.resolve(template: "{Artist}", for: track)
        XCTAssertEqual(result, "Raekwon")
    }

    func testResolveAlbumTemplate() {
        let track = makeTrack(album: "Only Built 4 Cuban Linx")
        let result = GroupTemplateResolver.resolve(template: "{Album}", for: track)
        XCTAssertEqual(result, "Only Built 4 Cuban Linx")
    }

    func testResolveGenreTemplate() {
        let track = makeTrack(genre: "Hip-Hop")
        let result = GroupTemplateResolver.resolve(template: "{Genre}", for: track)
        XCTAssertEqual(result, "Hip-Hop")
    }

    func testResolveDateTemplate() {
        let track = makeTrack(year: 1995)
        let result = GroupTemplateResolver.resolve(template: "{Date}", for: track)
        XCTAssertEqual(result, "1995")
    }

    func testResolveFormatTemplate() {
        let track = makeTrack(fileFormat: "FLAC")
        let result = GroupTemplateResolver.resolve(template: "{Format}", for: track)
        XCTAssertEqual(result, "FLAC")
    }

    func testResolveKeyTemplate() {
        let track = makeTrack(musicalKey: "Cm")
        let result = GroupTemplateResolver.resolve(template: "{Key}", for: track)
        XCTAssertEqual(result, "Cm")
    }

    // MARK: - Folder Resolution

    func testResolveFolderTemplate() {
        let track = makeTrack(filePath: "Music/Hip-Hop/Raekwon/track.mp3")
        let result = GroupTemplateResolver.resolve(template: "{Folder}", for: track)
        XCTAssertEqual(result, "Raekwon")
    }

    func testResolveFolderRootPath() {
        let track = makeTrack(filePath: "song.mp3")
        let result = GroupTemplateResolver.resolve(template: "{Folder}", for: track)
        XCTAssertEqual(result, "Root")
    }

    // MARK: - BPM Range

    func testResolveBPMRange() {
        let track = makeTrack(bpm: 125.0)
        let result = GroupTemplateResolver.resolve(template: "{BPM}", for: track)
        XCTAssertEqual(result, "120-130 BPM")
    }

    func testResolveBPMRangeExact() {
        let track = makeTrack(bpm: 130.0)
        let result = GroupTemplateResolver.resolve(template: "{BPM}", for: track)
        XCTAssertEqual(result, "130-140 BPM")
    }

    func testResolveBPMNil() {
        let track = makeTrack(bpm: nil)
        let result = GroupTemplateResolver.resolve(template: "{BPM}", for: track)
        XCTAssertEqual(result, "No BPM")
    }

    // MARK: - Compound Templates

    func testResolveAlbumDateTemplate() {
        let track = makeTrack(album: "Madvillainy", year: 2004)
        let result = GroupTemplateResolver.resolve(template: "{Album} ({Date})", for: track)
        XCTAssertEqual(result, "Madvillainy (2004)")
    }

    func testResolveArtistAlbumTemplate() {
        let track = makeTrack(artist: "MF DOOM", album: "Operation: Doomsday")
        let result = GroupTemplateResolver.resolve(template: "{Artist} — {Album}", for: track)
        XCTAssertEqual(result, "MF DOOM — Operation: Doomsday")
    }

    // MARK: - Missing/Empty Data Handling

    func testResolveEmptyTemplate() {
        let track = makeTrack()
        let result = GroupTemplateResolver.resolve(template: "", for: track)
        XCTAssertEqual(result, "")
    }

    func testResolveEmptyArtist() {
        let track = makeTrack(artist: "")
        let result = GroupTemplateResolver.resolve(template: "{Artist}", for: track)
        XCTAssertEqual(result, "Unknown Artist")
    }

    func testResolveEmptyAlbum() {
        let track = makeTrack(album: "")
        let result = GroupTemplateResolver.resolve(template: "{Album}", for: track)
        XCTAssertEqual(result, "Unknown Album")
    }

    func testResolveEmptyGenre() {
        let track = makeTrack(genre: "")
        let result = GroupTemplateResolver.resolve(template: "{Genre}", for: track)
        XCTAssertEqual(result, "Unknown Genre")
    }

    func testResolveEmptyFormat() {
        let track = makeTrack(fileFormat: "")
        let result = GroupTemplateResolver.resolve(template: "{Format}", for: track)
        XCTAssertEqual(result, "Unknown")
    }

    func testResolveNilKey() {
        let track = makeTrack(musicalKey: nil)
        let result = GroupTemplateResolver.resolve(template: "{Key}", for: track)
        XCTAssertEqual(result, "Unknown Key")
    }

    func testResolveNilDateCleansUpBrackets() {
        let track = makeTrack(album: "Some Album", year: nil)
        let result = GroupTemplateResolver.resolve(template: "{Album} ({Date})", for: track)
        // Empty parens should be cleaned up
        XCTAssertEqual(result, "Some Album")
    }

    func testResolveUnknownArtistCleanup() {
        let track = makeTrack(artist: "")
        let result = GroupTemplateResolver.resolve(template: "{Artist} — {Album}", for: track)
        // " — Unknown Artist" should be stripped — but since Artist is first, it stays
        // The cleanup rule is " — Unknown Artist" specifically
        XCTAssertFalse(result.isEmpty)
    }

    func testResolveAllEmptyFieldsReturnsUngrouped() {
        let track = Track(title: "Bare", filePath: "song.mp3")
        // With all empty, the resolved string may end up blank after cleanup
        let result = GroupTemplateResolver.resolve(template: "{Date}", for: track)
        // Year is nil → Date resolves to "" → template becomes "" → should return "Ungrouped"
        XCTAssertEqual(result, "Ungrouped")
    }

    // MARK: - Presets & Placeholders

    func testPresetsExist() {
        XCTAssertFalse(GroupTemplateResolver.presets.isEmpty)
        XCTAssertGreaterThanOrEqual(GroupTemplateResolver.presets.count, 8)
    }

    func testPresetsContainNoGrouping() {
        let noGrouping = GroupTemplateResolver.presets.first { $0.template.isEmpty }
        XCTAssertNotNil(noGrouping)
        XCTAssertEqual(noGrouping?.name, "No Grouping")
    }

    func testPlaceholdersExist() {
        XCTAssertEqual(GroupTemplateResolver.placeholders.count, 8)
    }

    func testAllPlaceholderTokensHaveBraces() {
        for placeholder in GroupTemplateResolver.placeholders {
            XCTAssertTrue(placeholder.token.hasPrefix("{"), "\(placeholder.token) should start with {")
            XCTAssertTrue(placeholder.token.hasSuffix("}"), "\(placeholder.token) should end with }")
        }
    }

    func testAllPlaceholderDescriptionsNonEmpty() {
        for placeholder in GroupTemplateResolver.placeholders {
            XCTAssertFalse(placeholder.description.isEmpty, "\(placeholder.token) should have a description")
        }
    }
}
