//
//  VoteLogicsTests.swift
//  ReflectTests
//
//  Tests for ShowBasedOnVoteLogics vote status and timing.
//

import XCTest
import SwiftData
@testable import Reflect

@MainActor
final class VoteLogicsTests: XCTestCase {

    // MARK: - Phase 7: passedTodaysVotingUnlock Tests

    func test_passedVotingUnlock_beforeTime() {
        // Voting unlock set to 6:00 PM, current time is 2:00 PM
        let voteDate = makeDateWithTime(hour: 18, minute: 0)
        let now = makeDateWithTime(hour: 14, minute: 0)

        let result = ShowBasedOnVoteLogics.passedTodaysVotingUnlock(voteDate: voteDate, now: now)
        XCTAssertFalse(result, "Should not have passed unlock when current time is before vote time")
    }

    func test_passedVotingUnlock_afterTime() {
        // Voting unlock set to 6:00 PM, current time is 8:00 PM
        let voteDate = makeDateWithTime(hour: 18, minute: 0)
        let now = makeDateWithTime(hour: 20, minute: 0)

        let result = ShowBasedOnVoteLogics.passedTodaysVotingUnlock(voteDate: voteDate, now: now)
        XCTAssertTrue(result, "Should have passed unlock when current time is after vote time")
    }

    func test_passedVotingUnlock_exactTime() {
        // Voting unlock set to 6:00 PM, current time is exactly 6:00 PM
        let voteDate = makeDateWithTime(hour: 18, minute: 0)
        let now = makeDateWithTime(hour: 18, minute: 0)

        let result = ShowBasedOnVoteLogics.passedTodaysVotingUnlock(voteDate: voteDate, now: now)
        XCTAssertTrue(result, "Should have passed unlock at exact vote time (uses >=)")
    }

    // MARK: - Phase 7: getCurrentVotingDate Tests

    func test_votingDate_today_beforeTime() {
        let onboarding = OnboardingData()
        onboarding.inputDay = .Today
        onboarding.date = makeDateWithTime(hour: 18, minute: 0)

        let now = makeDateWithTime(hour: 14, minute: 0) // before 6pm
        let result = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding, now: now)

        let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: now)!
        XCTAssertTrue(Calendar.current.isDate(result, inSameDayAs: yesterday),
                      "Today mode, before unlock: should return yesterday")
    }

    func test_votingDate_today_afterTime() {
        let onboarding = OnboardingData()
        onboarding.inputDay = .Today
        onboarding.date = makeDateWithTime(hour: 18, minute: 0)

        let now = makeDateWithTime(hour: 20, minute: 0) // after 6pm
        let result = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding, now: now)

        XCTAssertTrue(Calendar.current.isDate(result, inSameDayAs: now),
                      "Today mode, after unlock: should return today")
    }

    func test_votingDate_previous_beforeTime() {
        let onboarding = OnboardingData()
        onboarding.inputDay = .Previous
        onboarding.date = makeDateWithTime(hour: 18, minute: 0)

        let now = makeDateWithTime(hour: 14, minute: 0) // before 6pm
        let result = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding, now: now)

        let twoDaysAgo = Calendar.current.date(byAdding: .day, value: -2, to: now)!
        XCTAssertTrue(Calendar.current.isDate(result, inSameDayAs: twoDaysAgo),
                      "Previous mode, before unlock: should return 2 days ago")
    }

    func test_votingDate_previous_afterTime() {
        let onboarding = OnboardingData()
        onboarding.inputDay = .Previous
        onboarding.date = makeDateWithTime(hour: 18, minute: 0)

        let now = makeDateWithTime(hour: 20, minute: 0) // after 6pm
        let result = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding, now: now)

        let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: now)!
        XCTAssertTrue(Calendar.current.isDate(result, inSameDayAs: yesterday),
                      "Previous mode, after unlock: should return yesterday")
    }

    // MARK: - Phase 7: Vote Needed Tests (exercising the same logic as isMissingCurrentVote)
    // Note: isMissingCurrentVote uses DataController.shared (singleton) internally,
    // so we test the equivalent logic using a testable DataController instance.

    func test_voteNeeded_noEntry() {
        let sut = makeTestDataController()
        let onboarding = OnboardingData()
        onboarding.inputDay = .Today
        onboarding.date = makeDateWithTime(hour: 0, minute: 1)

        let votingDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding)
        let entry = sut.getEntry(byDate: votingDate.startOfDay)
        let isMissing = entry == nil || entry?.mood == .missing
        XCTAssertTrue(isMissing, "Should need a vote when no entry exists")
    }

    func test_voteNeeded_validEntry() {
        let sut = makeTestDataController()
        let onboarding = OnboardingData()
        onboarding.inputDay = .Today
        onboarding.date = makeDateWithTime(hour: 0, minute: 1)

        let votingDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding)
        sut.add(mood: .great, forDate: votingDate, entryType: .listView)

        let entry = sut.getEntry(byDate: votingDate.startOfDay)
        let isMissing = entry == nil || entry?.mood == .missing
        XCTAssertFalse(isMissing, "Should not need a vote when valid entry exists")
    }

    func test_voteNeeded_missingEntry() {
        let sut = makeTestDataController()
        let onboarding = OnboardingData()
        onboarding.inputDay = .Today
        onboarding.date = makeDateWithTime(hour: 0, minute: 1)

        let votingDate = ShowBasedOnVoteLogics.getCurrentVotingDate(onboardingData: onboarding)
        sut.add(mood: .missing, forDate: votingDate, entryType: .filledInMissing)

        let entry = sut.getEntry(byDate: votingDate.startOfDay)
        let isMissing = entry == nil || entry?.mood == .missing
        XCTAssertTrue(isMissing, "Should need a vote when entry is .missing")
    }

    // MARK: - Helpers

    private func makeTestDataController() -> DataController {
        let schema = Schema([MoodEntryModel.self])
        let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true)
        return DataController(container: try! ModelContainer(for: schema, configurations: [config]))
    }

    private func makeDateWithTime(hour: Int, minute: Int) -> Date {
        let calendar = Calendar.current
        var components = calendar.dateComponents([.year, .month, .day], from: Date())
        components.hour = hour
        components.minute = minute
        components.second = 0
        return calendar.date(from: components)!
    }
}
