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

local DATA = ns.Data or {}
local MIDNIGHT_SPARK_POOL = DATA.MIDNIGHT_SPARK_POOL or {}
local MIDNIGHT_SPARK_VARIANT_SUFFIX = DATA.MIDNIGHT_SPARK_VARIANT_SUFFIX or {}
local HOUSING_WEEKLY_POOL = DATA.HOUSING_WEEKLY_POOL or {}
local HOUSING_WEEKLY_TITLE_PATTERNS = DATA.HOUSING_WEEKLY_TITLE_PATTERNS or {}
local ROTATING_WORLD_WEEKLY_POOL = DATA.ROTATING_WORLD_WEEKLY_POOL or {}
local ROTATING_WORLD_WEEKLY_PATTERNS = DATA.ROTATING_WORLD_WEEKLY_PATTERNS or {}
local ROTATING_WORLD_AETHAS_POOL = DATA.ROTATING_WORLD_AETHAS_POOL or {}
local ROTATING_WORLD_AETHAS_FALLBACK_NAMES = DATA.ROTATING_WORLD_AETHAS_FALLBACK_NAMES or {}
local ROTATING_WORLD_AETHAS_PATTERNS = DATA.ROTATING_WORLD_AETHAS_PATTERNS or {}

local Util = ns.Util or {}
local safeCopyTable = Util.SafeCopyTable
local stringContainsAnyPattern = Util.StringContainsAnyPattern

local Quest = ns.Quest or {}
local isQuestCompleted = Quest.IsQuestCompleted
local isQuestActive = Quest.IsQuestActive
local getQuestObjectiveLines = Quest.GetQuestObjectiveLines
local getQuestLogEntries = Quest.GetQuestLogEntries
local isQuestTurnInReady = Quest.IsQuestTurnInReady
local getQuestObjectiveProgressText = Quest.GetQuestObjectiveProgressText
local getQuestTitle = Quest.GetQuestTitle

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

local function getActiveQuestStatusText(questID, readyToTurnIn, turnInText, openFallback)
    if readyToTurnIn then
        return turnInText or (L.STATUS_TURNIN or "Turn in")
    end

    if type(getQuestObjectiveProgressText) == "function" then
        local progressText = getQuestObjectiveProgressText(questID)
        if type(progressText) == "string" and progressText ~= "" then
            return progressText
        end
    end

    return openFallback or (L.STATUS_OPEN or "Accepted")
end

local function copyWorldWeeklyEntry(entry)
    if type(entry) ~= "table" then
        return nil
    end

    local copy = safeCopyTable(entry)
    if type(entry.tooltipLines) == "table" then
        local tooltipLines = {}
        for index, line in ipairs(entry.tooltipLines) do
            tooltipLines[index] = line
        end
        copy.tooltipLines = tooltipLines
    end

    return copy
end

local function getStoredWorldWeeklyRow(key, rowKey)
    if type(key) ~= "string" or key == "" or type(rowKey) ~= "string" or rowKey == "" then
        return nil
    end

    local storedState = ns:GetStoredWorldWeeklyState(key)
    local weeklyRows = type(storedState) == "table" and type(storedState.weeklyRows) == "table" and storedState.weeklyRows or nil
    return copyWorldWeeklyEntry(weeklyRows and weeklyRows[rowKey] or nil)
end

local function markHousingWeeklyEntry(entry)
    if type(entry) == "table" then
        entry.accountWide = true
        entry.renownAccountWide = true
    end
    return entry
end

local function buildHousingWeeklyRow()
    local memory = getWeeklyMemoryBucket("housing_weekly")
    local activeQuestID

    for _, questID in ipairs(HOUSING_WEEKLY_POOL) do
        if isQuestActive(questID) then
            activeQuestID = questID
            break
        end
    end

    if not activeQuestID then
        for _, entry in ipairs(getQuestLogEntries()) do
            if stringContainsAnyPattern(entry.title or entry.text, HOUSING_WEEKLY_TITLE_PATTERNS) then
                activeQuestID = tonumber(entry.questID) or activeQuestID
                if activeQuestID and activeQuestID > 0 then
                    break
                end
            end
        end
    end

    if activeQuestID and activeQuestID > 0 and memory then
        memory.questID = activeQuestID
        memory.title = getQuestTitle(activeQuestID, L.PVE_WEEKLY_ROW_HOUSING or "Housing Weekly")
    end

    local rememberedQuestID = memory and tonumber(memory.questID) or nil
    local label = L.PVE_WEEKLY_ROW_HOUSING or "Housing Weekly"
    local tooltipTitle = (activeQuestID and getQuestTitle(activeQuestID, label))
        or (rememberedQuestID and getQuestTitle(rememberedQuestID, label))
        or (memory and memory.title)
        or label

    if activeQuestID and activeQuestID > 0 then
        local readyToTurnIn = isQuestTurnInReady(activeQuestID)
        local tooltipLines = getQuestObjectiveLines(activeQuestID)
        if #tooltipLines == 0 then
            tooltipLines[1] = readyToTurnIn
                and (L.PVP_WEEKLY_TURNIN_TOOLTIP or "This quest is done and can now be turned in.")
                or (L.PVE_WEEKLY_HOUSING_ACTIVE_HINT or "This quest is active. Check the quest log for the current objective.")
        end

        return markHousingWeeklyEntry({
            key = "housing",
            label = label,
            text = getActiveQuestStatusText(activeQuestID, readyToTurnIn),
            status = readyToTurnIn and "turnin" or "open",
            seen = true,
            completed = false,
            questID = activeQuestID,
            turnInQuestID = readyToTurnIn and activeQuestID or nil,
            tooltipTitle = tooltipTitle,
            tooltipLines = tooltipLines,
        })
    end

    if rememberedQuestID and rememberedQuestID > 0 and isQuestCompleted(rememberedQuestID) then
        return markHousingWeeklyEntry({
            key = "housing",
            label = label,
            text = L.STATUS_DONE or "Done",
            status = "complete",
            seen = true,
            completed = true,
            questID = rememberedQuestID,
            tooltipTitle = tooltipTitle,
            tooltipLines = {
                L.PVE_WEEKLY_HOUSING_DONE_HINT or "Already completed this week.",
            },
        })
    end

    return markHousingWeeklyEntry({
        key = "housing",
        label = label,
        text = L.STATUS_MISSING or "Accept",
        status = "missing",
        seen = false,
        completed = false,
        tooltipTitle = label,
        tooltipLines = {
            L.PVE_WEEKLY_HOUSING_ACCEPT_HINT or "This quest can be accepted from Vaeli in Silvermoon.",
        },
    })
end

local function buildRememberedRotatingWeeklyRow(config)
    local memory = getWeeklyMemoryBucket(config.memoryKey)
    local activeQuestID

    for _, questID in ipairs(config.questPool or {}) do
        if isQuestActive(questID) then
            activeQuestID = questID
            break
        end
    end

    if not activeQuestID then
        for _, entry in ipairs(getQuestLogEntries()) do
            if stringContainsAnyPattern(entry.title or entry.text, config.titlePatterns or {}) then
                activeQuestID = tonumber(entry.questID) or activeQuestID
                if activeQuestID and activeQuestID > 0 then
                    break
                end
            end
        end
    end

    local label = config.label
    local title
    if activeQuestID and activeQuestID > 0 then
        title = getQuestTitle(activeQuestID, (config.fallbackNames and config.fallbackNames[activeQuestID]) or label)
        if memory then
            memory.questID = activeQuestID
            memory.title = title
        end
    end

    local rememberedQuestID = memory and tonumber(memory.questID) or nil
    if not title and rememberedQuestID and rememberedQuestID > 0 then
        title = getQuestTitle(rememberedQuestID, config.fallbackNames and config.fallbackNames[rememberedQuestID])
    end
    if not title and memory then
        title = memory.title
    end

    local tooltipTitle = (title and title ~= "") and string.format("%s - %s", label, title) or label

    if activeQuestID and activeQuestID > 0 then
        local readyToTurnIn = (config.allowTurnIn ~= false) and isQuestTurnInReady(activeQuestID)
        local tooltipLines = getQuestObjectiveLines(activeQuestID)
        if #tooltipLines == 0 then
            tooltipLines[1] = readyToTurnIn
                and (L.PVP_WEEKLY_TURNIN_TOOLTIP or "This quest is done and can now be turned in.")
                or (config.activeHint or "This quest is active. Check the quest log for the current objective.")
        end

        return {
            key = config.key,
            label = label,
            text = getActiveQuestStatusText(activeQuestID, readyToTurnIn),
            status = readyToTurnIn and "turnin" or "open",
            seen = true,
            completed = false,
            questID = activeQuestID,
            turnInQuestID = readyToTurnIn and activeQuestID or nil,
            tooltipTitle = tooltipTitle,
            tooltipLines = tooltipLines,
        }
    end

    if rememberedQuestID and rememberedQuestID > 0 and isQuestCompleted(rememberedQuestID) then
        return {
            key = config.key,
            label = label,
            text = L.STATUS_DONE or "Done",
            status = "complete",
            seen = true,
            completed = true,
            questID = rememberedQuestID,
            tooltipTitle = tooltipTitle,
            tooltipLines = {
                config.doneHint or (L.PVE_WEEKLY_HOUSING_DONE_HINT or "Already completed this week."),
            },
        }
    end

    return {
        key = config.key,
        label = label,
        text = L.STATUS_MISSING or "Accept",
        status = "missing",
        seen = false,
        completed = false,
        tooltipTitle = label,
        tooltipLines = {
            config.acceptHint or (L.PVE_WEEKLY_HOUSING_ACCEPT_HINT or "This quest can be accepted from Vaeli in Silvermoon."),
        },
    }
end

local function buildRotatingDungeonWeeklyRow()
    return buildRememberedRotatingWeeklyRow({
        key = "rotating_world_weekly",
        memoryKey = "rotating_world_weekly",
        label = L.PVE_WEEKLY_ROW_DUNGEON or "Weekly Dungeon",
        questPool = ROTATING_WORLD_WEEKLY_POOL,
        titlePatterns = ROTATING_WORLD_WEEKLY_PATTERNS,
        fallbackNames = {},
        allowTurnIn = false,
        acceptHint = L.PVE_WEEKLY_DUNGEON_ACCEPT_HINT or "This quest can be accepted from Halduron in Silvermoon.",
        activeHint = L.PVE_WEEKLY_DUNGEON_ACTIVE_HINT or "This quest is active. Check the quest log for the current objective.",
        doneHint = L.PVE_WEEKLY_DUNGEON_DONE_HINT or "Already completed this week.",
    })
end

local function buildAethasWeeklyRow()
    return buildRememberedRotatingWeeklyRow({
        key = "rotating_world_aethas",
        memoryKey = "rotating_world_aethas",
        label = L.PVE_WEEKLY_ROW_AETHAS or "Rotating (Archmage Aethas)",
        questPool = ROTATING_WORLD_AETHAS_POOL,
        titlePatterns = ROTATING_WORLD_AETHAS_PATTERNS,
        fallbackNames = ROTATING_WORLD_AETHAS_FALLBACK_NAMES,
        allowTurnIn = false,
        acceptHint = L.PVE_WEEKLY_AETHAS_ACCEPT_HINT or "This quest can be accepted from Archmage Aethas in Silvermoon.",
        activeHint = L.PVE_WEEKLY_AETHAS_ACTIVE_HINT or "This quest is active. Check the quest log for the current objective.",
        doneHint = L.PVE_WEEKLY_AETHAS_DONE_HINT or "Already completed this week.",
    })
end

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

    local baseLabel = L.PVE_WEEKLY_SPARK_LABEL or "Unity Against the Void (Lady Liadrin)"

    if not self:IsCurrentCharacterKey(key) then
        if type(fallbackEntry) == "table" then
            return fallbackEntry
        end
        local storedEntry = getStoredWorldWeeklyRow(key, "spark")
        if storedEntry then
            return storedEntry
        end
        return {
            key = "spark",
            label = baseLabel,
            text = L.STATUS_MISSING or "Accept",
            status = "missing",
            seen = false,
            completed = false,
            tooltipTitle = baseLabel,
            tooltipLines = { L.PVE_WEEKLY_SPARK_ACCEPT_HINT or "This quest can be accepted from Lady Liadrin in Silvermoon." },
        }
    end

    local activeQuestID
    local completedQuestID
    for _, questID in ipairs(MIDNIGHT_SPARK_POOL) do
        if isQuestCompleted(questID) then
            completedQuestID = questID
            break
        end
        if not activeQuestID and isQuestActive(questID) then
            activeQuestID = questID
        end
    end

    local chosenQuestID = activeQuestID or completedQuestID
    local suffix = chosenQuestID and MIDNIGHT_SPARK_VARIANT_SUFFIX[chosenQuestID] or nil
    local label = suffix and (baseLabel .. " - " .. suffix) or baseLabel

    if completedQuestID then
        return {
            key = "spark",
            label = label,
            text = L.STATUS_DONE or "Done",
            status = "complete",
            seen = true,
            completed = true,
            questID = completedQuestID,
            tooltipTitle = label,
            tooltipLines = { L.PVE_WEEKLY_SPARK_DONE_HINT or "Already completed this week." },
        }
    end

    if activeQuestID then
        local lines = getQuestObjectiveLines(activeQuestID)
        local readyToTurnIn = isQuestTurnInReady(activeQuestID)
        if #lines == 0 then
            lines[#lines + 1] = readyToTurnIn
                and (L.PVE_WEEKLY_SPARK_TURNIN_HINT or "This quest is done and can now be turned in.")
                or (L.PVE_WEEKLY_SPARK_ACTIVE_HINT or "This quest is active. Check the quest log for the current objective.")
        end
        return {
            key = "spark",
            label = label,
            text = getActiveQuestStatusText(activeQuestID, readyToTurnIn),
            status = readyToTurnIn and "turnin" or "open",
            seen = true,
            completed = false,
            questID = activeQuestID,
            turnInQuestID = readyToTurnIn and activeQuestID or nil,
            tooltipTitle = label,
            tooltipLines = lines,
        }
    end

    return {
        key = "spark",
        label = label,
        text = L.STATUS_MISSING or "Accept",
        status = "missing",
        seen = false,
        completed = false,
        tooltipTitle = label,
        tooltipLines = { L.PVE_WEEKLY_SPARK_ACCEPT_HINT or "This quest can be accepted from Lady Liadrin in Silvermoon." },
    }
end

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

    if self:IsCurrentCharacterKey(key) then
        return buildHousingWeeklyRow()
    end

    if type(fallbackEntry) == "table" then
        return markHousingWeeklyEntry(fallbackEntry)
    end

    local storedEntry = getStoredWorldWeeklyRow(key, "housing")
    if storedEntry then
        return markHousingWeeklyEntry(storedEntry)
    end

    return markHousingWeeklyEntry({
        key = "housing",
        label = L.PVE_WEEKLY_ROW_HOUSING or "Housing Weekly",
        text = L.STATUS_MISSING or "Accept",
        status = "missing",
        seen = false,
        completed = false,
        tooltipTitle = L.PVE_WEEKLY_ROW_HOUSING or "Housing Weekly",
        tooltipLines = {
            L.PVE_WEEKLY_HOUSING_ACCEPT_HINT or "This quest can be accepted from Vaeli in Silvermoon.",
        },
    })
end

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

    if self:IsCurrentCharacterKey(key) then
        return buildRotatingDungeonWeeklyRow()
    end

    if type(fallbackEntry) == "table" then
        return fallbackEntry
    end

    local storedEntry = getStoredWorldWeeklyRow(key, "rotating_world_weekly")
    if storedEntry then
        return storedEntry
    end

    return {
        key = "rotating_world_weekly",
        label = L.PVE_WEEKLY_ROW_DUNGEON or "Weekly Dungeon",
        text = L.STATUS_MISSING or "Accept",
        status = "missing",
        seen = false,
        completed = false,
        tooltipTitle = L.PVE_WEEKLY_ROW_DUNGEON or "Weekly Dungeon",
        tooltipLines = {
            L.PVE_WEEKLY_DUNGEON_ACCEPT_HINT or "This quest can be accepted from Halduron in Silvermoon.",
        },
    }
end

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

    if self:IsCurrentCharacterKey(key) then
        return buildAethasWeeklyRow()
    end

    if type(fallbackEntry) == "table" then
        return fallbackEntry
    end

    local storedEntry = getStoredWorldWeeklyRow(key, "rotating_world_aethas")
    if storedEntry then
        return storedEntry
    end

    return {
        key = "rotating_world_aethas",
        label = L.PVE_WEEKLY_ROW_AETHAS or "Rotating (Archmage Aethas)",
        text = L.STATUS_MISSING or "Accept",
        status = "missing",
        seen = false,
        completed = false,
        tooltipTitle = L.PVE_WEEKLY_ROW_AETHAS or "Rotating (Archmage Aethas)",
        tooltipLines = {
            L.PVE_WEEKLY_AETHAS_ACCEPT_HINT or "This quest can be accepted from Archmage Aethas in Silvermoon.",
        },
    }
end
