local _, ns = ...

local JOURNAL_STATE_KEYS = {
    raids = "raidJournal",
    dungeons = "dungeonJournal",
}

local JOURNAL_STATE_DEFAULTS = {
    raids = {
        selectedRaidKey = "dreamrift",
        selectedBossKey = "",
        difficultyKey = "normal",
        classFilterMode = "player",
        classFilterKey = "player",
    },
    dungeons = {
        selectedRaidKey = "",
        selectedBossKey = "",
        difficultyKey = "normal",
        classFilterMode = "player",
        classFilterKey = "player",
    },
}

local function normalizeJournalMainTab(mainTab)
    if mainTab == "dungeons" then
        return "dungeons"
    end

    return "raids"
end

function ns:GetJournalState(mainTab)
    local journalMainTab = normalizeJournalMainTab(mainTab)
    local defaults = JOURNAL_STATE_DEFAULTS[journalMainTab]
    if not self.db or not self.db.ui then
        return {
            selectedRaidKey = defaults.selectedRaidKey,
            selectedBossKey = defaults.selectedBossKey,
            difficultyKey = defaults.difficultyKey,
            classFilterMode = defaults.classFilterMode,
            classFilterKey = defaults.classFilterKey,
        }
    end

    local stateKey = JOURNAL_STATE_KEYS[journalMainTab]
    self.db.ui[stateKey] = type(self.db.ui[stateKey]) == "table" and self.db.ui[stateKey] or {}
    local state = self.db.ui[stateKey]
    state.selectedRaidKey = type(state.selectedRaidKey) == "string" and state.selectedRaidKey or defaults.selectedRaidKey
    state.selectedBossKey = type(state.selectedBossKey) == "string" and state.selectedBossKey or defaults.selectedBossKey
    state.difficultyKey = type(state.difficultyKey) == "string" and state.difficultyKey or defaults.difficultyKey
    state.classFilterKey = type(state.classFilterKey) == "string" and state.classFilterKey
        or ((state.classFilterMode == "all" or state.classFilterMode == "player") and state.classFilterMode or defaults.classFilterKey)
    state.classFilterMode = state.classFilterKey == "player" and "player" or (state.classFilterKey == "all" and "all" or "custom")
    return state
end

function ns:GetRaidJournalState()
    return self:GetJournalState("raids")
end

function ns:GetDungeonJournalState()
    return self:GetJournalState("dungeons")
end

local VALID_RAID_LOOT_DIFFICULTIES = {
    lfr = true,
    normal = true,
    heroic = true,
    mythic = true,
}

local VALID_RAID_LOOT_TRACKER_STATES = {
    wish = true,
    obtained = true,
}

local function normalizeRaidLootDifficultyKey(difficultyKey)
    if type(difficultyKey) == "string" and VALID_RAID_LOOT_DIFFICULTIES[difficultyKey] then
        return difficultyKey
    end

    return "normal"
end

local function normalizeRaidLootTrackerState(state)
    if type(state) == "string" and VALID_RAID_LOOT_TRACKER_STATES[state] then
        return state
    end

    return nil
end

function ns:GetRaidLootTrackerState(characterKey, difficultyKey, itemID)
    if type(characterKey) ~= "string" or characterKey == "" then
        return nil
    end

    local tracker = self.db and self.db.raidLootTracker
    if type(tracker) ~= "table" then
        return nil
    end

    local characterBucket = tracker[characterKey]
    if type(characterBucket) ~= "table" then
        return nil
    end

    local difficultyBucket = characterBucket[normalizeRaidLootDifficultyKey(difficultyKey)]
    if type(difficultyBucket) ~= "table" then
        return nil
    end

    local itemKey = tonumber(itemID)
    if not itemKey or itemKey <= 0 then
        return nil
    end

    return normalizeRaidLootTrackerState(difficultyBucket[tostring(itemKey)])
end

function ns:SetRaidLootTrackerState(characterKey, difficultyKey, itemID, state)
    if type(characterKey) ~= "string" or characterKey == "" then
        return
    end

    if type(self.db) ~= "table" then
        return
    end

    local itemKey = tonumber(itemID)
    if not itemKey or itemKey <= 0 then
        return
    end

    local trackerState = normalizeRaidLootTrackerState(state)
    self.db.raidLootTracker = type(self.db.raidLootTracker) == "table" and self.db.raidLootTracker or {}

    local difficultyBucket
    local characterBucket = self.db.raidLootTracker[characterKey]
    if type(characterBucket) ~= "table" then
        characterBucket = {}
        self.db.raidLootTracker[characterKey] = characterBucket
    end

    local difficultyKeySafe = normalizeRaidLootDifficultyKey(difficultyKey)
    difficultyBucket = characterBucket[difficultyKeySafe]
    if type(difficultyBucket) ~= "table" then
        difficultyBucket = {}
        characterBucket[difficultyKeySafe] = difficultyBucket
    end

    local itemKeyString = tostring(itemKey)
    if trackerState then
        difficultyBucket[itemKeyString] = trackerState
    else
        difficultyBucket[itemKeyString] = nil
        if next(difficultyBucket) == nil then
            characterBucket[difficultyKeySafe] = nil
        end
        if next(characterBucket) == nil then
            self.db.raidLootTracker[characterKey] = nil
        end
    end

    if self.UI and self.UI.Refresh then
        self.UI:Refresh()
    end
end

function ns:CycleRaidLootTrackerState(characterKey, difficultyKey, itemID)
    local currentState = self:GetRaidLootTrackerState(characterKey, difficultyKey, itemID)
    local nextState

    if currentState == "wish" then
        nextState = "obtained"
    elseif currentState == "obtained" then
        nextState = nil
    else
        nextState = "wish"
    end

    self:SetRaidLootTrackerState(characterKey, difficultyKey, itemID, nextState)
end

function ns:GetRaidLootTrackerEntries(characterKey, stateFilter)
    if type(characterKey) ~= "string" or characterKey == "" then
        return {}
    end

    local tracker = self.db and self.db.raidLootTracker
    if type(tracker) ~= "table" then
        return {}
    end

    local characterBucket = tracker[characterKey]
    if type(characterBucket) ~= "table" then
        return {}
    end

    local normalizedFilter = stateFilter and normalizeRaidLootTrackerState(stateFilter) or nil
    local entries = {}

    for difficultyKey, difficultyBucket in pairs(characterBucket) do
        if VALID_RAID_LOOT_DIFFICULTIES[difficultyKey] and type(difficultyBucket) == "table" then
            for itemKey, storedState in pairs(difficultyBucket) do
                local itemID = tonumber(itemKey)
                local normalizedState = normalizeRaidLootTrackerState(storedState)
                if itemID and itemID > 0 and normalizedState and (not normalizedFilter or normalizedState == normalizedFilter) then
                    entries[#entries + 1] = {
                        difficultyKey = difficultyKey,
                        itemID = itemID,
                        state = normalizedState,
                    }
                end
            end
        end
    end

    table.sort(entries, function(a, b)
        if (a.difficultyKey or "") ~= (b.difficultyKey or "") then
            return (a.difficultyKey or "") < (b.difficultyKey or "")
        end

        return (tonumber(a.itemID) or 0) < (tonumber(b.itemID) or 0)
    end)

    return entries
end

function ns:GetRaidLootTrackerCounts(characterKey)
    local counts = {
        wish = 0,
        obtained = 0,
    }

    for _, entry in ipairs(self:GetRaidLootTrackerEntries(characterKey)) do
        if entry.state == "wish" then
            counts.wish = counts.wish + 1
        elseif entry.state == "obtained" then
            counts.obtained = counts.obtained + 1
        end
    end

    return counts
end

function ns:SetJournalSelection(mainTab, raidKey, bossKey)
    if not self.db or not self.db.ui then
        return
    end

    local state = self:GetJournalState(mainTab)
    if type(raidKey) == "string" and raidKey ~= "" then
        state.selectedRaidKey = raidKey
    end
    if type(bossKey) == "string" then
        state.selectedBossKey = bossKey
    end

    if self.UI and self.UI.Refresh then
        self.UI:Refresh()
    end
end

function ns:SetRaidJournalSelection(raidKey, bossKey)
    self:SetJournalSelection("raids", raidKey, bossKey)
end

function ns:SetDungeonJournalSelection(raidKey, bossKey)
    self:SetJournalSelection("dungeons", raidKey, bossKey)
end

function ns:SetJournalDifficultyKey(mainTab, difficultyKey)
    if type(difficultyKey) ~= "string" or difficultyKey == "" then
        return
    end

    local state = self:GetJournalState(mainTab)
    state.difficultyKey = difficultyKey

    if self.UI and self.UI.Refresh then
        self.UI:Refresh()
    end
end

function ns:SetRaidJournalDifficultyKey(difficultyKey)
    self:SetJournalDifficultyKey("raids", difficultyKey)
end

function ns:SetDungeonJournalDifficultyKey(difficultyKey)
    self:SetJournalDifficultyKey("dungeons", difficultyKey)
end

function ns:SetJournalClassFilterMode(mainTab, mode)
    if type(mode) ~= "string" or mode == "" then
        return
    end

    self:SetJournalClassFilterKey(mainTab, mode)
end

function ns:SetRaidJournalClassFilterMode(mode)
    self:SetJournalClassFilterMode("raids", mode)
end

function ns:SetDungeonJournalClassFilterMode(mode)
    self:SetJournalClassFilterMode("dungeons", mode)
end

function ns:SetJournalClassFilterKey(mainTab, filterKey)
    if type(filterKey) ~= "string" or filterKey == "" then
        return
    end

    local state = self:GetJournalState(mainTab)
    state.classFilterKey = filterKey
    state.classFilterMode = filterKey == "player" and "player" or (filterKey == "all" and "all" or "custom")

    if self.UI and self.UI.Refresh then
        self.UI:Refresh()
    end
end

function ns:SetRaidJournalClassFilterKey(filterKey)
    self:SetJournalClassFilterKey("raids", filterKey)
end

function ns:SetDungeonJournalClassFilterKey(filterKey)
    self:SetJournalClassFilterKey("dungeons", filterKey)
end
