import Foundation
import Combine

/// Manages achievements, daily rewards, and daily quests.
class RetentionService: ObservableObject {
    @Published var achievements: [Achievement] = []
    @Published var unacknowledgedAchievements: [Achievement] = []
    @Published var recentAchievementUnlock: Achievement?

    @Published var dailyRewardState: DailyRewardState?
    @Published var dailyQuestsState: DailyQuestsState?

    /// Per-category achievement collection summary (locked + unlocked entries
    /// with hints + completion %). Rides inside the `achievementsList` message as
    /// `summary`. Powers the AchievementCollectionView.
    @Published var achievementSummary: AchievementSummary?

    /// Weak reference back to the facade for sending messages and accessing person.
    weak var facade: WebSocketService?

    func sendMessage(_ message: [String: Any]) {
        facade?.sendMessage(message: message)
    }

    // MARK: - Actions

    func fetchAchievements() {
        sendMessage(WebSocketCommands.getAchievements())
    }

    func acknowledgeAchievement(_ achievementId: String) {
        sendMessage(WebSocketCommands.acknowledgeAchievement(achievementId))
    }

    func fetchDailyRewards() {
        sendMessage(WebSocketCommands.getDailyRewards())
    }

    func claimDailyReward(day: Int) {
        sendMessage(WebSocketCommands.claimDailyReward(day: day))
    }

    func fetchDailyQuests() {
        sendMessage(WebSocketCommands.getDailyQuests())
    }

    func claimQuestReward(questId: String) {
        sendMessage(WebSocketCommands.claimQuestReward(questId: questId))
    }

    // MARK: - Message Handling

    /// Returns true if the message type was handled by this service.
    func handleMessage(type: String, data: [String: Any]) -> Bool {
        switch type {
        case "achievementsList":
            handleAchievementsList(data)
            return true
        case "achievementUnlocked":
            handleAchievementUnlocked(data)
            return true
        case "dailyRewardStatus":
            handleDailyRewardStatus(data)
            return true
        case "dailyRewardClaimed":
            handleDailyRewardClaimed(data)
            return true
        case "dailyQuestsStatus":
            handleDailyQuestsStatus(data)
            return true
        case "questProgress":
            handleQuestProgress(data)
            return true
        case "questRewardClaimed":
            handleQuestRewardClaimed(data)
            return true
        default:
            return false
        }
    }

    // MARK: - Private Handlers

    private func handleAchievementsList(_ data: [String: Any]) {
        if let achievementsData = data["achievements"] as? [[String: Any]],
           let jsonData = try? JSONSerialization.data(withJSONObject: achievementsData),
           let parsed = try? JSONDecoder().decode([Achievement].self, from: jsonData) {
            self.achievements = parsed
        }

        // Per-category collection summary (locked + unlocked entries, hints,
        // completion %) rides alongside the flat achievements list.
        if let summaryDict = data["summary"] as? [String: Any],
           let summaryJson = try? JSONSerialization.data(withJSONObject: summaryDict),
           let summary = try? JSONDecoder().decode(AchievementSummary.self, from: summaryJson) {
            self.achievementSummary = summary
        }
    }

    private func handleAchievementUnlocked(_ data: [String: Any]) {
        guard let achievementData = data["achievement"] as? [String: Any] else { return }

        let category = Achievement.AchievementCategory(rawValue: achievementData["category"] as? String ?? "Activities") ?? .activities
        let achievement = Achievement(
            id: achievementData["id"] as? String ?? UUID().uuidString,
            name: achievementData["name"] as? String ?? "Achievement",
            description: achievementData["description"] as? String ?? "",
            category: category,
            reward: achievementData["reward"] as? Int ?? 0,
            requirement: achievementData["requirement"] as? String ?? "Complete requirement",
            unlocked: true,
            unlockedAt: Date(),
            progress: nil,
            progressRequired: nil,
            acknowledged: false
        )

        if let index = self.achievements.firstIndex(where: { $0.id == achievement.id }) {
            self.achievements[index] = achievement
        } else {
            self.achievements.append(achievement)
        }

        if !achievement.acknowledged {
            self.unacknowledgedAchievements.append(achievement)
            self.facade?.showAchievementUnlock = true
        }

        ToastManager.shared.show(.success, message: "Achievement Unlocked: \(achievement.name)!")
        SoundManager.shared.playSound(.achievement)
    }

    private func handleDailyRewardStatus(_ data: [String: Any]) {
        if let jsonData = try? JSONSerialization.data(withJSONObject: data),
           let state = try? JSONDecoder().decode(DailyRewardState.self, from: jsonData) {
            self.dailyRewardState = state

            if state.canClaim && !state.todaysClaimed {
                self.facade?.showDailyRewards = true
            }
        }
    }

    private func handleDailyRewardClaimed(_ data: [String: Any]) {
        let success = data["success"] as? Bool
            ?? (data["result"] as? [String: Any])?["success"] as? Bool
            ?? false
        guard success else { return }

        #if DEBUG
        if facade == nil { print("RetentionService.handleDailyRewardClaimed: facade is nil, reward will be lost") }
        #endif

        let reward = data["reward"] as? [String: Any]
        let legacyResult = data["result"] as? [String: Any]

        if let diamondsEarned = reward?["diamonds"] as? Int ?? legacyResult?["diamonds_earned"] as? Int {
            facade?.person.diamonds += diamondsEarned
        }
        if let energyEarned = reward?["energy"] as? Int ?? legacyResult?["energy_earned"] as? Int {
            facade?.person.calcEnergy += energyEarned
        }

        fetchDailyRewards()

        ToastManager.shared.show(.success, message: "Daily reward claimed!")
        SoundManager.shared.playSound(.purchaseSuccess)
    }

    private func handleDailyQuestsStatus(_ data: [String: Any]) {
        if let jsonData = try? JSONSerialization.data(withJSONObject: data),
           let state = try? JSONDecoder().decode(DailyQuestsState.self, from: jsonData) {
            self.dailyQuestsState = state
        }
    }

    private func handleQuestProgress(_ data: [String: Any]) {
        if let quest = data["quest"] as? [String: Any],
           let questId = quest["id"] as? String {
            if let state = self.dailyQuestsState {
                let updatedQuests = state.quests.map { existing in
                    guard existing.id == questId else { return existing }
                    return DailyQuest(
                        id: existing.id,
                        name: existing.name,
                        description: existing.description,
                        category: existing.category,
                        reward: existing.reward,
                        progress: quest["progress"] as? Int ?? existing.progress,
                        target: quest["progressRequired"] as? Int ?? existing.target,
                        completed: quest["completed"] as? Bool ?? existing.completed,
                        claimed: existing.claimed
                    )
                }
                self.dailyQuestsState = DailyQuestsState(
                    quests: updatedQuests,
                    lastResetDate: state.lastResetDate,
                    nextResetDate: state.nextResetDate
                )
            }

            if quest["completed"] as? Bool == true,
               let questName = quest["description"] as? String {
                ToastManager.shared.show(.success, message: "Quest completed: \(questName)!")
            }
        } else if let jsonData = try? JSONSerialization.data(withJSONObject: data),
                  let state = try? JSONDecoder().decode(DailyQuestsState.self, from: jsonData) {
            self.dailyQuestsState = state
        }
    }

    private func handleQuestRewardClaimed(_ data: [String: Any]) {
        let success = data["success"] as? Bool
            ?? (data["result"] as? [String: Any])?["success"] as? Bool
            ?? false
        guard success else { return }

        #if DEBUG
        if facade == nil { print("RetentionService.handleQuestRewardClaimed: facade is nil, reward will be lost") }
        #endif

        let reward = data["reward"] as? [String: Any]
        let legacyResult = data["result"] as? [String: Any]

        if let diamondsEarned = reward?["diamonds"] as? Int ?? legacyResult?["diamonds_earned"] as? Int {
            facade?.person.diamonds += diamondsEarned
        }
        if let energyEarned = reward?["energy"] as? Int ?? legacyResult?["energy_earned"] as? Int {
            facade?.person.calcEnergy += energyEarned
        }
        if let moneyEarned = reward?["money"] as? Double ?? legacyResult?["money_earned"] as? Double {
            facade?.person.money += Int(moneyEarned)
        } else if let moneyEarned = reward?["money"] as? Int ?? legacyResult?["money_earned"] as? Int {
            facade?.person.money += moneyEarned
        }

        fetchDailyQuests()

        ToastManager.shared.show(.success, message: "Quest reward claimed!")
        SoundManager.shared.playSound(.purchaseSuccess)
    }
}
