//
//  StreakTests.swift
//  ReflectTests
//
//  Tests for DataController streak calculation.
//

import XCTest
import SwiftData
@testable import Reflect

@MainActor
final class StreakTests: XCTestCase {
    var sut: DataController!

    override func setUp() {
        super.setUp()
        let schema = Schema([MoodEntryModel.self])
        let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true)
        sut = DataController(container: try! ModelContainer(for: schema, configurations: [config]))
    }

    override func tearDown() {
        sut = nil
        super.tearDown()
    }

    // MARK: - Phase 6: Streak Calculation

    func test_streak_emptyDB_zero() {
        let result = sut.calculateStreak(from: Date())
        XCTAssertEqual(result.streak, 0)
        XCTAssertNil(result.todaysMood)
    }

    func test_streak_singleEntryToday_one() {
        let today = Calendar.current.startOfDay(for: Date())
        sut.add(mood: .great, forDate: today, entryType: .listView)

        let result = sut.calculateStreak(from: today)
        XCTAssertEqual(result.streak, 1)
        XCTAssertEqual(result.todaysMood, .great)
    }

    func test_streak_consecutiveDays() {
        let today = Calendar.current.startOfDay(for: Date())
        for i in 0..<5 {
            let date = Calendar.current.date(byAdding: .day, value: -i, to: today)!
            sut.add(mood: .good, forDate: date, entryType: .listView)
        }

        let result = sut.calculateStreak(from: today)
        XCTAssertEqual(result.streak, 5)
    }

    func test_streak_gapBreaks() {
        let today = Calendar.current.startOfDay(for: Date())
        // Today, yesterday, then skip a day, then 2 more
        sut.add(mood: .good, forDate: today, entryType: .listView)
        sut.add(mood: .good, forDate: Calendar.current.date(byAdding: .day, value: -1, to: today)!, entryType: .listView)
        // Skip day -2
        sut.add(mood: .good, forDate: Calendar.current.date(byAdding: .day, value: -3, to: today)!, entryType: .listView)
        sut.add(mood: .good, forDate: Calendar.current.date(byAdding: .day, value: -4, to: today)!, entryType: .listView)

        let result = sut.calculateStreak(from: today)
        XCTAssertEqual(result.streak, 2, "Streak should stop at the gap")
    }

    func test_streak_missingDoesNotCount() {
        let today = Calendar.current.startOfDay(for: Date())
        sut.add(mood: .good, forDate: today, entryType: .listView)
        // Yesterday is a .missing entry (should be ignored)
        sut.add(mood: .missing, forDate: Calendar.current.date(byAdding: .day, value: -1, to: today)!, entryType: .filledInMissing)
        sut.add(mood: .good, forDate: Calendar.current.date(byAdding: .day, value: -2, to: today)!, entryType: .listView)

        let result = sut.calculateStreak(from: today)
        XCTAssertEqual(result.streak, 1, ".missing entries should not count toward streak")
    }

    func test_streak_noEntryToday_countsFromYesterday() {
        let today = Calendar.current.startOfDay(for: Date())
        let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: today)!
        let twoDaysAgo = Calendar.current.date(byAdding: .day, value: -2, to: today)!

        sut.add(mood: .good, forDate: yesterday, entryType: .listView)
        sut.add(mood: .great, forDate: twoDaysAgo, entryType: .listView)

        let result = sut.calculateStreak(from: today)
        XCTAssertEqual(result.streak, 2, "Should count from yesterday when today has no entry")
        XCTAssertNil(result.todaysMood, "Today's mood should be nil when no entry exists")
    }

    func test_streak_entryAfterVotingTime_stillCounts() {
        // Bug: calculateStreak passes votingDate (with time) as endDate to getData.
        // getData uses <=, so an entry logged AFTER votingDate's time is excluded.
        // E.g. if votingDate is "today at 8am" and user logged at 10am, the 10am entry
        // is excluded from the query, making streak miss today.
        let today = Calendar.current.startOfDay(for: Date())
        let morningVotingDate = Calendar.current.date(byAdding: .hour, value: 8, to: today)!

        // Add entry at 10am (after the 8am voting date time)
        let afternoonDate = Calendar.current.date(byAdding: .hour, value: 10, to: today)!
        sut.add(mood: .great, forDate: afternoonDate, entryType: .listView)

        let result = sut.calculateStreak(from: morningVotingDate)
        XCTAssertEqual(result.streak, 1, "Entry logged after votingDate time should still count")
        XCTAssertEqual(result.todaysMood, .great, "Today's mood should be found even if logged after votingDate time")
    }

    func test_streak_afterDelete_decreases() {
        let today = Calendar.current.startOfDay(for: Date())
        let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: today)!

        sut.add(mood: .good, forDate: today, entryType: .listView)
        sut.add(mood: .great, forDate: yesterday, entryType: .listView)

        let beforeDelete = sut.calculateStreak(from: today)
        XCTAssertEqual(beforeDelete.streak, 2)

        sut.deleteAllEntries(forDate: yesterday)

        let afterDelete = sut.calculateStreak(from: today)
        XCTAssertEqual(afterDelete.streak, 1, "Streak should decrease after deleting a day")
    }
}
