local _, ns = ...
local L = ns.L or {}

local Util = ns.Util or {}
local getWeeklyResetInfo = Util.GetWeeklyResetInfo

local DATA = ns.Data or {}
local PREY_WEEKLY_QUEST_ID = DATA.PREY_WEEKLY_QUEST_ID or 94446
local PREY_WEEKLY_QUEST_TARGET = DATA.PREY_WEEKLY_QUEST_TARGET or 3
local PREY_PREFERRED_QUEST_ID = DATA.PREY_PREFERRED_QUEST_ID or 91277
local PREY_PREFERRED_QUEST_TARGET = DATA.PREY_PREFERRED_QUEST_TARGET or 1
local PREY_RENOWN_FACTION_ID = DATA.PREY_RENOWN_FACTION_ID or 2764
local PREY_DIFFICULTY_CAP = DATA.PREY_DIFFICULTY_CAP or 4
local PREY_DIFFICULTY_ORDER = DATA.PREY_DIFFICULTY_ORDER or {}
local PREY_TARGET_QUESTS = DATA.PREY_TARGET_QUESTS or {}
local PREY_DIFFICULTY_RENOWN = DATA.PREY_DIFFICULTY_RENOWN or {}
local PREY_STATE_SCHEMA_VERSION = 2

local Quest = ns.Quest or {}
local isQuestCompleted = Quest.IsQuestCompleted
local getQuestObjectiveLines = Quest.GetQuestObjectiveLines
local getQuestObjectiveProgress = Quest.GetQuestObjectiveProgress

local WeeklyMemory = ns.WeeklyMemory or {}
local getWeeklyMemoryBucket = WeeklyMemory.GetWeeklyMemoryBucket

local WoWApi = ns.WoWApi or {}
local getCurrentRenownLevel = WoWApi.GetCurrentRenownLevel
local getActivePreyMapName = WoWApi.GetActivePreyMapName

local PREY_WEEKLY_MEMORY_KEY = "prey_weekly_quest"
local PREY_PREFERRED_MEMORY_KEY = "prey_preferred_quest"

local function getUnlockedPreyDifficultyKeys(renownLevel)
    local unlocked = {}
    renownLevel = math.max(0, tonumber(renownLevel) or 0)

    for _, difficultyKey in ipairs(PREY_DIFFICULTY_ORDER) do
        if renownLevel >= (PREY_DIFFICULTY_RENOWN[difficultyKey] or 0) then
            unlocked[#unlocked + 1] = difficultyKey
        end
    end

    return unlocked
end

local function normalizeLegacyPreyState(prey)
    if type(prey) ~= "table" then
        return nil
    end

    local difficultyCap = math.max(0, tonumber(prey.difficultyCap) or PREY_DIFFICULTY_CAP)
    local totalCap = math.max(0, tonumber(prey.cap) or (difficultyCap * #PREY_DIFFICULTY_ORDER))

    return {
        total = math.max(0, tonumber(prey.total) or 0),
        totalCap = totalCap,
        difficultyCap = difficultyCap,
        unlockedCount = math.max(1, math.floor(totalCap / math.max(1, difficultyCap))),
        difficulties = {
            normal = {
                count = math.max(0, tonumber(prey.normal) or 0),
                cap = difficultyCap,
                unlocked = true,
            },
            hard = {
                count = math.max(0, tonumber(prey.hard) or 0),
                cap = difficultyCap,
                unlocked = true,
            },
            nightmare = {
                count = math.max(0, tonumber(prey.nightmare) or 0),
                cap = difficultyCap,
                unlocked = true,
            },
        },
    }
end

local function getCurrentPreyWeeklyResetAt(owner)
    if type(getWeeklyResetInfo) == "function" then
        local _, weeklyResetAt = getWeeklyResetInfo()
        weeklyResetAt = tonumber(weeklyResetAt)
        if weeklyResetAt and weeklyResetAt > 0 then
            return weeklyResetAt
        end
    end

    if owner and type(owner.GetWeeklyResetSeconds) == "function" then
        local weeklyResetSeconds = math.max(0, tonumber(owner:GetWeeklyResetSeconds()) or 0)
        if weeklyResetSeconds > 0 then
            return time() + weeklyResetSeconds
        end
    end

    return nil
end

local function mergePreyStateWithExisting(state, existing)
    if type(state) ~= "table" or type(existing) ~= "table" then
        return state
    end

    local total = 0
    for _, difficultyKey in ipairs(PREY_DIFFICULTY_ORDER) do
        local current = state.difficulties and state.difficulties[difficultyKey]
        local old = existing.difficulties and existing.difficulties[difficultyKey]
        if type(current) == "table" and current.unlocked ~= false then
            local cap = math.max(0, tonumber(current.cap) or PREY_DIFFICULTY_CAP)
            local currentCount = math.max(0, tonumber(current.count) or 0)
            local oldCount = type(old) == "table" and math.max(0, tonumber(old.count) or 0) or 0
            if oldCount > currentCount then
                current.count = cap > 0 and math.min(oldCount, cap) or oldCount
            end
            total = total + math.max(0, tonumber(current.count) or 0)
        end
    end
    state.total = total

    if type(existing.weeklyQuest) == "table"
        and existing.weeklyQuest.completed == true
        and type(state.weeklyQuest) == "table"
        and state.weeklyQuest.completed ~= true
    then
        state.weeklyQuest = existing.weeklyQuest
    end

    if type(existing.preferredQuest) == "table"
        and existing.preferredQuest.completed == true
        and type(state.preferredQuest) == "table"
        and state.preferredQuest.completed ~= true
    then
        state.preferredQuest = existing.preferredQuest
    end

    return state
end

function ns:BuildCurrentPreyState()
    local renownLevel = getCurrentRenownLevel(PREY_RENOWN_FACTION_ID)
    local unlockedDifficulties = getUnlockedPreyDifficultyKeys(renownLevel)
    local unlockedLookup = {}
    local activeMapName, activeQuestID = getActivePreyMapName()
    local state = {
        renownLevel = renownLevel,
        activeMapName = activeMapName,
        activeQuestID = activeQuestID,
        total = 0,
        totalCap = math.max(PREY_DIFFICULTY_CAP, #unlockedDifficulties * PREY_DIFFICULTY_CAP),
        difficultyCap = PREY_DIFFICULTY_CAP,
        unlockedCount = #unlockedDifficulties,
        difficulties = {},
    }

    for _, difficultyKey in ipairs(unlockedDifficulties) do
        unlockedLookup[difficultyKey] = true
    end

    for _, difficultyKey in ipairs(PREY_DIFFICULTY_ORDER) do
        local count = 0
        for _, questID in ipairs(PREY_TARGET_QUESTS[difficultyKey] or {}) do
            if isQuestCompleted(questID) then
                count = count + 1
            end
        end

        local isUnlocked = unlockedLookup[difficultyKey] == true
        state.difficulties[difficultyKey] = {
            count = count,
            cap = PREY_DIFFICULTY_CAP,
            unlocked = isUnlocked,
        }

        if isUnlocked then
            state.total = state.total + count
        end
    end

    if state.unlockedCount <= 0 then
        state.unlockedCount = 1
    end

    return state
end

local function buildCurrentPreyWeeklyQuestState()
    local live = getQuestObjectiveProgress(PREY_WEEKLY_QUEST_ID, PREY_WEEKLY_QUEST_TARGET)
    local lines = {}
    local memory = getWeeklyMemoryBucket and getWeeklyMemoryBucket(PREY_WEEKLY_MEMORY_KEY) or nil

    if live.completed == true and type(memory) == "table" then
        memory.completed = true
        memory.questID = PREY_WEEKLY_QUEST_ID
        memory.completedAt = memory.completedAt or time()
    elseif live.seen ~= true and type(memory) == "table" and memory.completed == true then
        live.seen = true
        live.completed = true
        live.status = "complete"
        live.count = PREY_WEEKLY_QUEST_TARGET
        live.text = L.STATUS_DONE or "Done"
        live.questID = PREY_WEEKLY_QUEST_ID
        live.turnInQuestID = nil
    end

    if not live.seen then
        lines[#lines + 1] = L.PVE_PREY_WEEKLY_ACCEPT_HINT or "This quest can be accepted from Astalor in Silvermoon."
    elseif live.status == "turnin" then
        lines[#lines + 1] = L.PVE_PREY_WEEKLY_TURNIN_HINT or "Turn this quest in to Astalor in Silvermoon."
    elseif live.completed then
        lines[#lines + 1] = L.PVE_PREY_WEEKLY_DONE_HINT or "Already completed this week."
    else
        lines = getQuestObjectiveLines(PREY_WEEKLY_QUEST_ID)
        if #lines == 0 then
            lines[#lines + 1] = L.PVE_PREY_WEEKLY_ACTIVE_HINT or "This quest is active. Check the quest log for the current objective."
        end
    end

    live.tooltipTitle = L.PVE_PREY_WEEKLY_NAME or "A Nightmarish Task"
    live.tooltipLines = lines
    return live
end

local function buildCurrentPreyPreferredQuestState()
    local live = getQuestObjectiveProgress(PREY_PREFERRED_QUEST_ID, PREY_PREFERRED_QUEST_TARGET)
    local lines = {}
    local memory = getWeeklyMemoryBucket and getWeeklyMemoryBucket(PREY_PREFERRED_MEMORY_KEY) or nil

    if live.completed == true and type(memory) == "table" then
        memory.completed = true
        memory.questID = PREY_PREFERRED_QUEST_ID
        memory.completedAt = memory.completedAt or time()
    elseif live.seen ~= true and type(memory) == "table" and memory.completed == true then
        live.seen = true
        live.completed = true
        live.status = "complete"
        live.count = PREY_PREFERRED_QUEST_TARGET
        live.text = L.STATUS_DONE or "Done"
        live.questID = PREY_PREFERRED_QUEST_ID
        live.turnInQuestID = nil
    end

    if not live.seen then
        lines[#lines + 1] = L.PVE_PREY_WEEKLY_ACCEPT_HINT or "This quest can be accepted from Astalor in Silvermoon."
    elseif live.status == "turnin" then
        lines[#lines + 1] = L.PVE_PREY_WEEKLY_TURNIN_HINT or "Turn this quest in to Astalor in Silvermoon."
    elseif live.completed then
        lines[#lines + 1] = L.PVE_PREY_WEEKLY_DONE_HINT or "Already completed this week."
    else
        lines = getQuestObjectiveLines(PREY_PREFERRED_QUEST_ID)
        if #lines == 0 then
            lines[#lines + 1] = L.PVE_PREY_WEEKLY_ACTIVE_HINT or "This quest is active. Check the quest log for the current objective."
        end
    end

    live.tooltipTitle = L.PVE_PREY_PREFERRED_NAME or "Preferred Prey"
    live.tooltipLines = lines
    return live
end

function ns:RefreshCurrentCharacterPreyData()
    local record = self:RefreshCurrentCharacterRecord()
    self:ClearExpiredPreyState(record)
    local existingState = type(record.prey) == "table" and record.prey.state or nil

    local state = self:BuildCurrentPreyState()
    if not state then
        return nil
    end

    state.weeklyQuest = buildCurrentPreyWeeklyQuestState()
    state.preferredQuest = buildCurrentPreyPreferredQuestState()
    state = mergePreyStateWithExisting(state, existingState)

    local resetAt = getCurrentPreyWeeklyResetAt(self)
    record.prey = {
        schemaVersion = PREY_STATE_SCHEMA_VERSION,
        resetAt = resetAt,
        weekKey = resetAt,
        capturedAt = time(),
        state = state,
    }

    return state
end

function ns:GetLivePreyState(keyOrCharacter, fallbackEntry)
    local key = keyOrCharacter
    if type(keyOrCharacter) == "table" then
        key = keyOrCharacter.key
    end

    if self:IsCurrentCharacterKey(key) then
        local liveState = self:BuildCurrentPreyState()
        if liveState then
            return mergePreyStateWithExisting(liveState, self:GetStoredPreyState(key))
        end
    end

    local storedState = self:GetStoredPreyState(key)
    if storedState then
        return storedState
    end

    return normalizeLegacyPreyState(fallbackEntry)
end

function ns:GetLivePreyWeeklyQuestState(keyOrCharacter, fallbackEntry)
    local key = keyOrCharacter
    if type(keyOrCharacter) == "table" then
        key = keyOrCharacter.key
    end

    if not self:IsCurrentCharacterKey(key) then
        local storedState = self:GetStoredPreyState(key)
        if type(storedState) == "table" and type(storedState.weeklyQuest) == "table" then
            return storedState.weeklyQuest
        end
        return fallbackEntry
    end

    local liveState = buildCurrentPreyWeeklyQuestState()
    local storedState = self:GetStoredPreyState(key)
    if type(storedState) == "table"
        and type(storedState.weeklyQuest) == "table"
        and storedState.weeklyQuest.completed == true
        and liveState.completed ~= true
    then
        return storedState.weeklyQuest
    end

    return liveState
end

function ns:RememberPreyQuestTurnIn(questID)
    questID = tonumber(questID)
    local memoryKey
    if questID == PREY_WEEKLY_QUEST_ID then
        memoryKey = PREY_WEEKLY_MEMORY_KEY
    elseif questID == PREY_PREFERRED_QUEST_ID then
        memoryKey = PREY_PREFERRED_MEMORY_KEY
    end

    if not memoryKey or not getWeeklyMemoryBucket then
        return
    end

    local memory = getWeeklyMemoryBucket(memoryKey)
    if type(memory) ~= "table" then
        return
    end

    memory.completed = true
    memory.questID = questID
    memory.completedAt = memory.completedAt or time()
end

function ns:GetLivePreyPreferredQuestState(keyOrCharacter, fallbackEntry)
    local key = keyOrCharacter
    if type(keyOrCharacter) == "table" then
        key = keyOrCharacter.key
    end

    if not self:IsCurrentCharacterKey(key) then
        local storedState = self:GetStoredPreyState(key)
        if type(storedState) == "table" and type(storedState.preferredQuest) == "table" then
            return storedState.preferredQuest
        end
        return fallbackEntry
    end

    local liveState = buildCurrentPreyPreferredQuestState()
    local storedState = self:GetStoredPreyState(key)
    if type(storedState) == "table"
        and type(storedState.preferredQuest) == "table"
        and storedState.preferredQuest.completed == true
        and liveState.completed ~= true
    then
        return storedState.preferredQuest
    end

    return liveState
end
