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

local DATA = ns.Data or {}
local DELVE_TROVEHUNTERS_BOUNTY_ITEM_ID = DATA.DELVE_TROVEHUNTERS_BOUNTY_ITEM_ID or 252415
local DELVE_TROVEHUNTERS_BOUNTY_FLAG_QUEST_ID = DATA.DELVE_TROVEHUNTERS_BOUNTY_FLAG_QUEST_ID or 86371
local DELVE_TROVEHUNTERS_UNLOCK_FACTION_ID = DATA.DELVE_TROVEHUNTERS_UNLOCK_FACTION_ID or 2722
local DELVE_TROVEHUNTERS_UNLOCK_RENOWN = DATA.DELVE_TROVEHUNTERS_UNLOCK_RENOWN or 2
local DELVE_GILDED_STASH_SPELL_ID = DATA.DELVE_GILDED_STASH_SPELL_ID or 1216211
local DELVE_GILDED_STASH_WIDGET_ID = DATA.DELVE_GILDED_STASH_WIDGET_ID or 7591
local DELVE_BONUS_RENOWN_FLAGS = DATA.DELVE_BONUS_RENOWN_FLAGS or {}
local DELVE_WEEKLY_DROP_QUEST_ID = DATA.DELVE_WEEKLY_DROP_QUEST_ID or 93784

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

local WoWApi = ns.WoWApi or {}
local getCurrentRenownLevel = WoWApi.GetCurrentRenownLevel
local getMajorFactionNameByID = WoWApi.GetMajorFactionNameByID
local getSpellNameByID = WoWApi.GetSpellNameByID
local getItemCountByID = WoWApi.GetItemCountByID

local Quest = ns.Quest or {}
local isQuestCompleted = Quest.IsQuestCompleted
local isQuestCompletedOnAccount = Quest.IsQuestCompletedOnAccount
local isQuestActive = Quest.IsQuestActive
local isQuestTurnInReady = Quest.IsQuestTurnInReady
local getQuestObjectiveLines = Quest.GetQuestObjectiveLines
local parseObjectiveFraction = Quest.ParseObjectiveFraction

local WeeklyMemory = ns.WeeklyMemory or {}
local getRememberedWeeklyMemory = WeeklyMemory.GetRememberedWeeklyMemory
local rememberWeeklyCompletion = WeeklyMemory.RememberWeeklyCompletion

local DELVE_WEEKLY_DROP_MEMORY_KEY = "delve_weekly_drop"

local function getDelveGildedStashWidgetState()
    if not (C_UIWidgetManager and type(C_UIWidgetManager.GetSpellDisplayVisualizationInfo) == "function") then
        return nil
    end

    local ok, info = pcall(C_UIWidgetManager.GetSpellDisplayVisualizationInfo, DELVE_GILDED_STASH_WIDGET_ID)
    if not ok or type(info) ~= "table" then
        return nil
    end

    local spellInfo = type(info.spellInfo) == "table" and info.spellInfo or nil
    if not spellInfo or tonumber(spellInfo.spellID) ~= DELVE_GILDED_STASH_SPELL_ID then
        return nil
    end

    local current, maximum = parseObjectiveFraction(spellInfo.tooltip or "")
    return {
        current = current,
        maximum = maximum,
        isAccurate = spellInfo.shownState == 1,
        tooltip = spellInfo.tooltip,
        spellName = getSpellNameByID(DELVE_GILDED_STASH_SPELL_ID, nil),
    }
end

local function getDelvesStateRow(state, rowKey)
    if type(state) ~= "table" or type(state.rows) ~= "table" then
        return nil
    end

    for _, row in ipairs(state.rows) do
        if type(row) == "table" and row.key == rowKey then
            return row
        end
    end

    return nil
end

local function buildDelveTrovehuntersBountyRow()
    local label = L.PVE_DELVES_ROW_TROVEHUNTER or "Treasure Seeker's Bounty"
    local itemCount = getItemCountByID(DELVE_TROVEHUNTERS_BOUNTY_ITEM_ID)
    local used = isQuestCompleted(DELVE_TROVEHUNTERS_BOUNTY_FLAG_QUEST_ID)
    local unlocked = getCurrentRenownLevel(DELVE_TROVEHUNTERS_UNLOCK_FACTION_ID) >= DELVE_TROVEHUNTERS_UNLOCK_RENOWN
        or itemCount > 0
        or used
    local tooltipLines = {}
    local text
    local status

    if itemCount > 0 then
        text = L.PVE_DELVES_READY or "Ready"
        status = "open"
        tooltipLines[#tooltipLines + 1] = L.PVE_DELVES_TROVEHUNTER_OPEN_HINT
            or "You still have this week's treasure map in your bags."
    elseif used then
        text = L.STATUS_DONE or "Done"
        status = "complete"
        tooltipLines[#tooltipLines + 1] = L.PVE_DELVES_TROVEHUNTER_DONE_HINT
            or "This week's treasure map has already been used."
    elseif unlocked then
        text = L.PVE_DELVES_NOT_EARNED or "Not earned"
        status = "failed"
        tooltipLines[#tooltipLines + 1] = L.PVE_DELVES_TROVEHUNTER_MISSING_HINT
            or "You have not earned this week's treasure map yet."
    else
        text = L.STATUS_LOCKED or "Locked"
        status = "locked"
        tooltipLines[#tooltipLines + 1] = string.format(
            L.PVE_DELVES_TROVEHUNTER_LOCKED_HINT
                or "Unlocks at Renown %d with the Delvers.",
            DELVE_TROVEHUNTERS_UNLOCK_RENOWN
        )
    end

    if itemCount > 0 then
        tooltipLines[#tooltipLines + 1] = string.format(
            L.PVE_DELVES_INVENTORY_COUNT or "Currently in bags: %d",
            itemCount
        )
    end

    return {
        key = "delve_trovehunters_bounty",
        label = label,
        text = text,
        status = status,
        seen = unlocked,
        completed = used and itemCount <= 0,
        itemID = DELVE_TROVEHUNTERS_BOUNTY_ITEM_ID,
        tooltipTitle = label,
        tooltipLines = tooltipLines,
    }
end

local function buildDelveBonusRenownRow()
    local label = L.PVE_DELVES_ROW_BONUS_RENOWN or "Bonus Renown"
    local completedCount = 0
    local tooltipLines = {}

    for _, entry in ipairs(DELVE_BONUS_RENOWN_FLAGS) do
        local factionName = getMajorFactionNameByID(entry.factionID, entry.fallbackName)
        local completed = isQuestCompletedOnAccount(entry.questID)
        if completed then
            completedCount = completedCount + 1
        end
        tooltipLines[#tooltipLines + 1] = string.format(
            "%s: %s",
            factionName,
            completed and (L.STATUS_DONE or "Done") or (L.PVE_DELVES_READY or "Ready")
        )
    end

    tooltipLines[#tooltipLines + 1] = " "
    tooltipLines[#tooltipLines + 1] = L.PVE_DELVES_BONUS_RENOWN_HINT
        or "Earn these four one-time weekly bonus renown rewards from bountiful delve chests."

    return {
        key = "delve_bonus_renown",
        label = label,
        text = string.format("%d/%d", completedCount, #DELVE_BONUS_RENOWN_FLAGS),
        status = completedCount >= #DELVE_BONUS_RENOWN_FLAGS and "complete" or "open",
        seen = true,
        completed = completedCount >= #DELVE_BONUS_RENOWN_FLAGS,
        count = completedCount,
        maxCount = #DELVE_BONUS_RENOWN_FLAGS,
        tooltipTitle = label,
        tooltipLines = tooltipLines,
    }
end

local function buildDelveWeeklyDropRow()
    local label = L.PVE_DELVES_ROW_WEEKLY_DROP or "Weekly Drop"
    local remembered = getRememberedWeeklyMemory and getRememberedWeeklyMemory(DELVE_WEEKLY_DROP_MEMORY_KEY) or nil
    local completed = isQuestCompletedOnAccount(DELVE_WEEKLY_DROP_QUEST_ID)
        or isQuestCompleted(DELVE_WEEKLY_DROP_QUEST_ID)
        or type(remembered) == "table"
    local active = isQuestActive and isQuestActive(DELVE_WEEKLY_DROP_QUEST_ID)
    local readyToTurnIn = active and isQuestTurnInReady and isQuestTurnInReady(DELVE_WEEKLY_DROP_QUEST_ID)
    local tooltipLines = {}

    tooltipLines[#tooltipLines + 1] = L.PVE_DELVES_WEEKLY_DROP_HINT
        or "Drops from the final boss in a delve and can be turned in once per week for your warband."

    if active and not completed then
        local objectiveLines = getQuestObjectiveLines and getQuestObjectiveLines(DELVE_WEEKLY_DROP_QUEST_ID) or {}
        if #objectiveLines > 0 then
            tooltipLines[#tooltipLines + 1] = " "
            for _, line in ipairs(objectiveLines) do
                tooltipLines[#tooltipLines + 1] = line
            end
        end
    end

    if completed then
        return {
            key = "delve_weekly_drop",
            label = label,
            text = L.STATUS_DONE or "Done",
            status = "complete",
            seen = true,
            completed = true,
            questID = DELVE_WEEKLY_DROP_QUEST_ID,
            accountWide = true,
            renownAccountWide = true,
            tooltipTitle = label,
            tooltipLines = tooltipLines,
        }
    end

    if readyToTurnIn then
        return {
            key = "delve_weekly_drop",
            label = label,
            text = L.STATUS_TURNIN or "Turn in",
            status = "turnin",
            seen = true,
            completed = false,
            questID = DELVE_WEEKLY_DROP_QUEST_ID,
            turnInQuestID = DELVE_WEEKLY_DROP_QUEST_ID,
            accountWide = true,
            renownAccountWide = true,
            tooltipTitle = label,
            tooltipLines = tooltipLines,
        }
    end

    if active then
        return {
            key = "delve_weekly_drop",
            label = label,
            text = L.STATUS_OPEN or "Accepted",
            status = "open",
            seen = true,
            completed = false,
            questID = DELVE_WEEKLY_DROP_QUEST_ID,
            accountWide = true,
            renownAccountWide = true,
            tooltipTitle = label,
            tooltipLines = tooltipLines,
        }
    end

    return {
        key = "delve_weekly_drop",
        label = label,
        text = L.PVE_DELVES_NOT_EARNED or "Not earned",
        status = "missing",
        seen = false,
        completed = false,
        questID = DELVE_WEEKLY_DROP_QUEST_ID,
        accountWide = true,
        renownAccountWide = true,
        tooltipTitle = label,
        tooltipLines = tooltipLines,
    }
end

local function buildDelveGildedStashRow(existingState)
    local label = L.PVE_DELVES_ROW_GILDED_STASH or "Gilded Stash"
    local widgetState = getDelveGildedStashWidgetState()
    local tooltipTitle = widgetState and widgetState.spellName or label
    local tooltipLines = {
        L.PVE_DELVES_GILDED_STASH_HINT
            or "Tracks how many gilded delve rewards from tier 11 are still available this week.",
    }

    if widgetState and type(widgetState.tooltip) == "string" and widgetState.tooltip ~= "" then
        tooltipLines[#tooltipLines + 1] = " "
        tooltipLines[#tooltipLines + 1] = widgetState.tooltip
        if widgetState.isAccurate == false then
            tooltipLines[#tooltipLines + 1] = L.PVE_DELVES_GILDED_STASH_STALE_HINT
                or "The game may still be showing older widget data until it refreshes."
        end
    else
        tooltipLines[#tooltipLines + 1] = L.PVE_DELVES_GILDED_STASH_NO_DATA
            or "No live gilded stash data is currently available."
    end

    local current = widgetState and tonumber(widgetState.current) or nil
    local maximum = widgetState and tonumber(widgetState.maximum) or nil
    if current and maximum and maximum > 0 then
        return {
            key = "delve_gilded_stash",
            label = label,
            text = string.format("%d/%d", current, maximum),
            status = current >= maximum and "complete" or "open",
            seen = true,
            completed = current >= maximum,
            count = current,
            maxCount = maximum,
            tooltipTitle = tooltipTitle,
            tooltipLines = tooltipLines,
        }
    end

    local storedRow = getDelvesStateRow(existingState, "delve_gilded_stash")
    local storedCount = tonumber(storedRow and storedRow.count)
    local storedMax = tonumber(storedRow and storedRow.maxCount)
    if storedCount and storedMax and storedMax > 0 then
        local preservedRow = safeCopyTable(storedRow)
        preservedRow.tooltipTitle = tooltipTitle or preservedRow.tooltipTitle
        preservedRow.tooltipLines = tooltipLines
        return preservedRow
    end

    return {
        key = "delve_gilded_stash",
        label = label,
        text = "?" .. "/" .. tostring(L.PVE_DELVES_GILDED_STASH_MAX or 4),
        status = "locked",
        seen = false,
        completed = false,
        tooltipTitle = tooltipTitle,
        tooltipLines = tooltipLines,
    }
end

local function buildDelvesSummary(rows)
    local completedRows = 0
    local totalRows = 0

    for _, row in ipairs(rows or {}) do
        if row then
            totalRows = totalRows + 1
            if row.status == "complete" or row.completed == true then
                completedRows = completedRows + 1
            end
        end
    end

    return {
        completed = completedRows,
        total = totalRows,
        text = string.format("%d/%d", completedRows, totalRows),
    }
end

local function applyGlobalDelvesRows(state)
    if type(state) ~= "table" or type(state.rows) ~= "table" then
        return state
    end

    local rows = {}
    local replacedBonusRenown = false
    local hasWeeklyDrop = false

    for _, row in ipairs(state.rows) do
        if type(row) == "table" and row.key == "delve_bonus_renown" then
            rows[#rows + 1] = buildDelveBonusRenownRow()
            replacedBonusRenown = true
        elseif type(row) == "table" and row.key == "delve_weekly_drop" then
            rows[#rows + 1] = buildDelveWeeklyDropRow()
            hasWeeklyDrop = true
        elseif type(row) == "table" then
            rows[#rows + 1] = safeCopyTable(row)
        end
    end

    if not hasWeeklyDrop then
        table.insert(rows, math.min(2, #rows + 1), buildDelveWeeklyDropRow())
    end

    if not replacedBonusRenown then
        rows[#rows + 1] = buildDelveBonusRenownRow()
    end

    local mergedState = safeCopyTable(state)
    mergedState.rows = rows
    mergedState.summary = buildDelvesSummary(rows)
    return mergedState
end

function ns:BuildCurrentDelvesState(existingState)
    local rows = {
        buildDelveTrovehuntersBountyRow(),
        buildDelveWeeklyDropRow(),
        buildDelveBonusRenownRow(),
        buildDelveGildedStashRow(existingState),
    }

    return {
        rows = rows,
        summary = buildDelvesSummary(rows),
    }
end

function ns:RememberDelveQuestTurnIn(questID)
    questID = tonumber(questID)
    if questID ~= DELVE_WEEKLY_DROP_QUEST_ID or type(rememberWeeklyCompletion) ~= "function" then
        return
    end

    rememberWeeklyCompletion(DELVE_WEEKLY_DROP_MEMORY_KEY, questID)
end

function ns:RefreshCurrentCharacterDelvesData()
    local record = self:RefreshCurrentCharacterRecord()
    self:ClearExpiredDelvesState(record)
    local existingState = type(record.delves) == "table" and type(record.delves.state) == "table" and record.delves.state or nil

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

    local _, weeklyResetAt = getWeeklyResetInfo()
    record.delves = {
        resetAt = weeklyResetAt,
        state = state,
    }

    return state
end

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

    if self:IsCurrentCharacterKey(key) then
        local liveState = self:RefreshCurrentCharacterDelvesData()
        if liveState then
            return applyGlobalDelvesRows(liveState)
        end
    end

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

    if type(fallbackState) == "table" then
        return applyGlobalDelvesRows(fallbackState)
    end

    return fallbackState
end
