import Testing
import Foundation
@testable import nahbar

// MARK: - CallWindowManager Tests

@Suite("CallWindowManager – Fenster-Logik")
struct CallWindowManagerTests {

    // Testet die reine Zeitfenster-Berechnung ohne den Singleton zu benutzen.
    // Die Logik: nowMin >= startMin && nowMin < endMin

    private func isInWindow(nowHour: Int, nowMin: Int,
                            startHour: Int, startMin: Int,
                            endHour: Int, endMin: Int) -> Bool {
        let nowMinutes   = nowHour * 60 + nowMin
        let startMinutes = startHour * 60 + startMin
        let endMinutes   = endHour * 60 + endMin
        return nowMinutes >= startMinutes && nowMinutes < endMinutes
    }

    @Test("Exakt zu Fensterbeginn ist innerhalb")
    func exactlyAtStartIsInWindow() {
        #expect(isInWindow(nowHour: 17, nowMin: 0,
                           startHour: 17, startMin: 0,
                           endHour: 18, endMin: 0))
    }

    @Test("Eine Minute vor Ende ist innerhalb")
    func oneMinuteBeforeEndIsInWindow() {
        #expect(isInWindow(nowHour: 17, nowMin: 59,
                           startHour: 17, startMin: 0,
                           endHour: 18, endMin: 0))
    }

    @Test("Exakt zum Fensterende ist außerhalb")
    func exactlyAtEndIsOutside() {
        #expect(!isInWindow(nowHour: 18, nowMin: 0,
                            startHour: 17, startMin: 0,
                            endHour: 18, endMin: 0))
    }

    @Test("Eine Minute vor Start ist außerhalb")
    func oneMinuteBeforeStartIsOutside() {
        #expect(!isInWindow(nowHour: 16, nowMin: 59,
                            startHour: 17, startMin: 0,
                            endHour: 18, endMin: 0))
    }

    @Test("Mitten im Fenster ist innerhalb")
    func middleOfWindowIsInside() {
        #expect(isInWindow(nowHour: 17, nowMin: 30,
                           startHour: 17, startMin: 0,
                           endHour: 18, endMin: 0))
    }

    @Test("Fenster mit Minuten korrekt berechnet")
    func windowWithMinutesIsCorrect() {
        // Fenster: 17:30 – 17:45
        #expect(isInWindow(nowHour: 17, nowMin: 30,
                           startHour: 17, startMin: 30,
                           endHour: 17, endMin: 45))
        #expect(isInWindow(nowHour: 17, nowMin: 44,
                           startHour: 17, startMin: 30,
                           endHour: 17, endMin: 45))
        #expect(!isInWindow(nowHour: 17, nowMin: 45,
                            startHour: 17, startMin: 30,
                            endHour: 17, endMin: 45))
    }

    @Test("Leeres Fenster (start == end) enthält niemanden")
    func emptyWindowContainsNobody() {
        #expect(!isInWindow(nowHour: 17, nowMin: 0,
                            startHour: 17, startMin: 0,
                            endHour: 17, endMin: 0))
    }
}

// MARK: - windowDescription Tests

@Suite("CallWindowManager – windowDescription")
struct CallWindowDescriptionTests {

    // windowDescription = String(format: "%02d:%02d – %02d:%02d Uhr", ...)

    private func description(startH: Int, startM: Int, endH: Int, endM: Int) -> String {
        String(format: "%02d:%02d – %02d:%02d Uhr", startH, startM, endH, endM)
    }

    @Test("Standard-Fenster 17:00 – 18:00")
    func standardWindowDescription() {
        #expect(description(startH: 17, startM: 0, endH: 18, endM: 0) == "17:00 – 18:00 Uhr")
    }

    @Test("Minuten werden mit führender Null formatiert")
    func minutesHaveLeadingZero() {
        #expect(description(startH: 9, startM: 5, endH: 10, endM: 0) == "09:05 – 10:00 Uhr")
    }

    @Test("Stunden werden mit führender Null formatiert")
    func hoursHaveLeadingZero() {
        #expect(description(startH: 8, startM: 0, endH: 9, endM: 0) == "08:00 – 09:00 Uhr")
    }
}

// MARK: - selectPerson Logik Tests

@Suite("CallWindowManager – selectPerson Logik")
struct SelectPersonLogicTests {

    // Testet die Selektions-Logik ohne den Manager zu instanziieren.
    // Logik: Filter → Prioritize needsAttention → Sort → Random top 3

    @Test("Leere Personenliste gibt nil zurück")
    func emptyListReturnsNil() {
        let persons: [Person] = []
        let result = persons.isEmpty ? nil as Person? : persons.first
        #expect(result == nil)
    }

    @Test("Person die heute vorgeschlagen wurde wird gefiltert")
    func personSuggestedTodayIsFiltered() {
        let p = Person(name: "Test")
        p.lastSuggestedForCall = Date()  // heute

        let candidates = [p].filter { person in
            if let last = person.lastSuggestedForCall,
               Calendar.current.isDateInToday(last) { return false }
            return true
        }
        #expect(candidates.isEmpty)
    }

    @Test("Person die heute noch nicht vorgeschlagen wurde ist Kandidat")
    func personNotSuggestedTodayIsCandidate() {
        let p = Person(name: "Test")
        p.lastSuggestedForCall = Calendar.current.date(byAdding: .day, value: -8, to: Date())

        let candidates = [p].filter { person in
            if let last = person.lastSuggestedForCall,
               Calendar.current.isDateInToday(last) { return false }
            if let last = person.lastSuggestedForCall {
                let days = Calendar.current.dateComponents([.day], from: last, to: Date()).day ?? 0
                if days < 7 { return false }
            }
            return true
        }
        #expect(!candidates.isEmpty)
    }

    @Test("Person innerhalb der 7-Tage-Sperre wird ausgeschlossen")
    func personWithin7DayCooldownIsExcluded() {
        let p = Person(name: "Test")
        p.lastSuggestedForCall = Calendar.current.date(byAdding: .day, value: -3, to: Date())

        let candidates = [p].filter { person in
            if let last = person.lastSuggestedForCall {
                let days = Calendar.current.dateComponents([.day], from: last, to: Date()).day ?? 0
                if days < 7 { return false }
            }
            return true
        }
        #expect(candidates.isEmpty)
    }

    @Test("needsAttention-Personen werden priorisiert")
    func needsAttentionPersonsArePrioritized() {
        let p1 = Person(name: "Vernachlässigt", nudgeFrequency: .weekly)
        p1.createdAt = Calendar.current.date(byAdding: .day, value: -20, to: Date())!

        let p2 = Person(name: "Aktuell", nudgeFrequency: .weekly)
        p2.createdAt = Date()

        let persons = [p2, p1]
        let prioritized = persons.filter { $0.needsAttention }
        #expect(prioritized.contains(where: { $0.name == "Vernachlässigt" }))
        #expect(!prioritized.contains(where: { $0.name == "Aktuell" }))
    }
}
