import Combine
import Foundation

// MARK: - Action states

enum MainAction: Equatable {
    case continueSession(kbTitle: String, elapsed: String)
    case todaysReview(count: Int, estimatedMinutes: Int)
    case selfTest(quizId: String, count: Int)
    case startLearning(knowledgeBaseId: String, kbCount: Int)
    case empty
}

enum HomeLoadingState: Equatable {
    case idle
    case loading
    case loaded
    case error(String)
}

// MARK: - ViewModel

@MainActor
final class StudyHomeViewModel: ObservableObject {
    @Published var loadingState: HomeLoadingState = .idle
    @Published var mainAction: MainAction?
    @Published var todayReviewCount = 0
    @Published var todayReviewEstimatedMinutes = 0
    @Published var availableQuizCount = 0
    @Published var weeklyMinutes = 0
    @Published var weeklyCardsReviewed = 0
    @Published var weeklyActiveDays = 0
    @Published var streakDays = 0
    @Published var banner: String?

    private var firstQuizId: String?
    private var firstKbId: String?

    func loadAll() async {
        loadingState = .loading
        banner = nil

        async let sessions = try? LearningSessionService.shared.list()
        async let dueCards = try? ReviewService.shared.dueCards()
        async let quizzes = try? QuizService.shared.list()
        async let knowledgeBases = try? KnowledgeBaseService.shared.list()
        async let summary = try? ActivityService.shared.summary()
        async let streak = try? ActivityService.shared.streak()

        let (sRes, dRes, qRes, kRes, sumRes, stRes) = await (sessions, dueCards, quizzes, knowledgeBases, summary, streak)

        let sessionsResult = sRes ?? []
        let dueCardsResult = dRes ?? []
        let quizzesResult = qRes ?? []
        let kbResult = kRes ?? []

        // Store first IDs for navigation
        firstQuizId = quizzesResult.first?.id
        firstKbId = kbResult.first?.id

        // Evaluate main action
        mainAction = evaluatePriority(
            sessions: sessionsResult,
            dueCards: dueCardsResult,
            quizzes: quizzesResult,
            knowledgeBases: kbResult
        )

        // Stats
        todayReviewCount = dueCardsResult.count
        todayReviewEstimatedMinutes = max(1, dueCardsResult.count)
        availableQuizCount = quizzesResult.count
        weeklyMinutes = sumRes?.totalMinutes ?? 0
        weeklyCardsReviewed = sumRes?.totalCardsReviewed ?? 0
        weeklyActiveDays = sumRes?.activeDays ?? 0
        streakDays = stRes?.currentStreak ?? 0

        // Banner
        banner = computeBanner(
            sessions: sessionsResult,
            quizzes: quizzesResult,
            dueCards: dueCardsResult
        )

        loadingState = .loaded
    }

    func refresh() async {
        async let sessions = try? LearningSessionService.shared.list()
        async let dueCards = try? ReviewService.shared.dueCards()
        async let quizzes = try? QuizService.shared.list()
        async let knowledgeBases = try? KnowledgeBaseService.shared.list()
        async let summary = try? ActivityService.shared.summary()
        async let streak = try? ActivityService.shared.streak()

        let (sRes, dRes, qRes, kRes, sumRes, stRes) = await (sessions, dueCards, quizzes, knowledgeBases, summary, streak)

        let sessionsResult = sRes ?? []
        let dueCardsResult = dRes ?? []
        let quizzesResult = qRes ?? []
        let kbResult = kRes ?? []

        firstQuizId = quizzesResult.first?.id
        firstKbId = kbResult.first?.id

        mainAction = evaluatePriority(
            sessions: sessionsResult,
            dueCards: dueCardsResult,
            quizzes: quizzesResult,
            knowledgeBases: kbResult
        )

        todayReviewCount = dueCardsResult.count
        todayReviewEstimatedMinutes = max(1, dueCardsResult.count)
        availableQuizCount = quizzesResult.count
        weeklyMinutes = sumRes?.totalMinutes ?? 0
        weeklyCardsReviewed = sumRes?.totalCardsReviewed ?? 0
        weeklyActiveDays = sumRes?.activeDays ?? 0
        streakDays = stRes?.currentStreak ?? 0
    }

    // MARK: - Priority Logic

    private func evaluatePriority(
        sessions: [LearningSession],
        dueCards: [ReviewCard],
        quizzes: [Quiz],
        knowledgeBases: [KnowledgeBase]
    ) -> MainAction {
        // Priority 1: Unfinished session
        if let unfinished = sessions
            .filter({ $0.status != nil && $0.status != "completed" })
            .sorted(by: { ($0.startedAt ?? "") > ($1.startedAt ?? "") })
            .first {
            let kbTitle = knowledgeBases.first(where: { $0.id == unfinished.knowledgeBaseId })?.title ?? "学习"
            let elapsed = formatElapsedSince(unfinished.startedAt)
            return .continueSession(kbTitle: kbTitle, elapsed: elapsed)
        }

        // Priority 2: Today's review
        if !dueCards.isEmpty {
            return .todaysReview(count: dueCards.count, estimatedMinutes: max(1, dueCards.count))
        }

        // Priority 3: Self-test
        if let quizId = firstQuizId, !quizzes.isEmpty {
            return .selfTest(quizId: quizId, count: quizzes.count)
        }

        // Priority 4: Start learning
        if let kbId = firstKbId, !knowledgeBases.isEmpty {
            return .startLearning(knowledgeBaseId: kbId, kbCount: knowledgeBases.count)
        }

        // Priority 5: Empty
        return .empty
    }

    // MARK: - Banner

    private func computeBanner(
        sessions: [LearningSession],
        quizzes: [Quiz],
        dueCards: [ReviewCard]
    ) -> String? {
        // No banner needed if nothing is happening
        if dueCards.isEmpty && quizzes.isEmpty && sessions.allSatisfy({ $0.status == "completed" }) {
            return nil
        }
        return nil
    }

    // MARK: - Helpers

    private func formatElapsedSince(_ iso: String?) -> String {
        guard let iso else { return "最近" }
        let formatter = ISO8601DateFormatter()
        formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
        guard let date = formatter.date(from: iso) else {
            // Try without fractional seconds
            formatter.formatOptions = [.withInternetDateTime]
            guard let date2 = formatter.date(from: iso) else { return "最近" }
            return relativeTime(from: date2)
        }
        return relativeTime(from: date)
    }

    private func relativeTime(from date: Date) -> String {
        let interval = Date().timeIntervalSince(date)
        let minutes = Int(interval / 60)
        if minutes < 1 { return "刚刚" }
        if minutes < 60 { return "\(minutes) 分钟前" }
        let hours = minutes / 60
        if hours < 24 { return "\(hours) 小时前" }
        let days = hours / 24
        return "\(days) 天前"
    }
}
