local addonName, FN = ...
FN = FN or FavoriteNeighborhood or {}
FavoriteNeighborhood = FN

FN.AutoSwitch = FN.AutoSwitch or {}
local AutoSwitch = FN.AutoSwitch

local SWITCH_COOLDOWN = 3
local BURST_WINDOW = 15
local BURST_LIMIT = 3
local BURST_LOCKOUT = 20
local lastSwitchAt = 0
local lastSwitchFrom = nil
local lastSwitchTo = nil
local recentSwitches = {}
local lockoutUntil = 0
local lastSeenCompleteState = {}

local function now()
    if GetTime then return GetTime() end
    return 0
end

local function refreshOptions()
    if FN and FN.RefreshOptionsIfVisible then
        FN.RefreshOptionsIfVisible()
    elseif FN and FN.UpdateOptionsFrame and FN.OptionsFrame and FN.OptionsFrame:IsShown() then
        FN.UpdateOptionsFrame()
    end
end

local function pruneRecentSwitches(t)
    for i = #recentSwitches, 1, -1 do
        if (t - recentSwitches[i]) > BURST_WINDOW then
            table.remove(recentSwitches, i)
        end
    end
end

function AutoSwitch:GetSelectableNeighborhoods()
    if FN.Debug and FN.Debug.IsEnabled and FN.Debug:IsEnabled() and FN.Debug.GetDropdownNeighborhoods then
        local ids = {}
        for _, item in ipairs(FN.Debug:GetDropdownNeighborhoods() or {}) do
            if item and item.value then ids[#ids+1] = tostring(item.value) end
        end
        return ids
    end
    local ids = {}
    local seen = {}
    local function add(id)
        if not id then return end
        id = tostring(id)
        if seen[id] then return end
        seen[id] = true
        ids[#ids + 1] = id
    end

    for id in pairs(FN_DB and FN_DB.neighborhoods or {}) do
        add(id)
    end

    table.sort(ids, function(a, b)
        local an = FN.ResolveNeighborhoodName and FN.ResolveNeighborhoodName(a) or a
        local bn = FN.ResolveNeighborhoodName and FN.ResolveNeighborhoodName(b) or b
        return tostring(an) < tostring(bn)
    end)

    return ids
end

function AutoSwitch:GetProgressData(id)
    if FN.GetProgressData then
        return FN.GetProgressData(id)
    end
    return FN_DB and FN_DB.progress and FN_DB.progress[tostring(id)] or nil
end

function AutoSwitch:IsComplete(id)
    if not id then return false end
    local info = self:GetProgressData(id)
    if not info then return false end
    if info.isComplete ~= nil then return info.isComplete and true or false end
    if tonumber(info.percent) then return tonumber(info.percent) >= 100 end
    local current = tonumber(info.currentProgress) or 0
    local maxv = tonumber(info.maxProgress) or 0
    return maxv > 0 and current >= maxv
end

function AutoSwitch:HasIncomplete(ids)
    ids = ids or self:GetSelectableNeighborhoods()
    for _, id in ipairs(ids) do
        if not self:IsComplete(id) then
            return true
        end
    end
    return false
end

function AutoSwitch:AreAllComplete(ids)
    ids = ids or self:GetSelectableNeighborhoods()
    if #ids == 0 then return false end
    for _, id in ipairs(ids) do
        if not self:IsComplete(id) then
            return false
        end
    end
    return true
end

function AutoSwitch:ShouldShowSwitchRecommended(currentID, ids)
    if not currentID then return false end
    ids = ids or self:GetSelectableNeighborhoods()
    if not self:IsComplete(currentID) then return false end
    for _, id in ipairs(ids) do
        if tostring(id) ~= tostring(currentID) and not self:IsComplete(id) then
            return true
        end
    end
    return false
end

function AutoSwitch:GetNextIncomplete(currentID, ids)
    ids = ids or self:GetSelectableNeighborhoods()
    if #ids == 0 then return nil end

    local startIndex = 0
    for i, id in ipairs(ids) do
        if tostring(id) == tostring(currentID) then
            startIndex = i
            break
        end
    end

    for step = 1, #ids do
        local idx = ((startIndex + step - 1) % #ids) + 1
        local id = ids[idx]
        if tostring(id) ~= tostring(currentID) and not self:IsComplete(id) then
            return id
        end
    end

    return nil
end

function AutoSwitch:IsEnabledForPlayer()
    return FN_DB and FN_DB.config and FN_DB.config.autoSwitchGlobal and true or false
end

function AutoSwitch:GetPreferredTarget(currentID, ids)
    ids = ids or self:GetSelectableNeighborhoods()
    local favorite = FN_DB and FN_DB.global or nil
    if not favorite then
        return currentID
    end

    if #ids == 0 then
        return favorite
    end

    if not self:IsComplete(favorite) then
        return tostring(favorite)
    end

    for _, id in ipairs(ids) do
        if tostring(id) ~= tostring(favorite) and not self:IsComplete(id) then
            return tostring(id)
        end
    end

    return tostring(favorite)
end

function AutoSwitch:EvaluateTarget(currentID, ids)
    return self:GetPreferredTarget(currentID, ids)
end

function AutoSwitch:CanSwitchNow(currentID, targetID, opts)
    opts = opts or {}
    if not self:IsEnabledForPlayer() then return false end
    if not currentID or not targetID then return false end
    if tostring(currentID) == tostring(targetID) then return false end
    local t = now()
    if lockoutUntil > t then return false end
    if (not opts.ignoreCooldown) and (t - lastSwitchAt) < SWITCH_COOLDOWN then return false end
    pruneRecentSwitches(t)
    if #recentSwitches >= BURST_LIMIT then
        lockoutUntil = t + BURST_LOCKOUT
        return false
    end
    return true
end

function AutoSwitch:TrySwitch(currentID, ids, opts)
    opts = opts or {}
    ids = ids or self:GetSelectableNeighborhoods()
    local targetID = self:EvaluateTarget(currentID, ids)
    if not self:CanSwitchNow(currentID, targetID, opts) then return false end

    if (not opts.ignoreCooldown) and lastSwitchTo and tostring(targetID) == tostring(lastSwitchTo) and (now() - lastSwitchAt) < SWITCH_COOLDOWN then
        return false
    end

    if FN.SetNeighborhood and FN.SetNeighborhood(targetID) then
        lastSwitchAt = now()
        lastSwitchFrom = currentID and tostring(currentID) or nil
        lastSwitchTo = tostring(targetID)
        table.insert(recentSwitches, lastSwitchAt)
        pruneRecentSwitches(lastSwitchAt)

        if FN.RequestNeighborhoodProgress then
            FN.RequestNeighborhoodProgress(targetID)
        end
        refreshOptions()
        return true
    end

    return false
end


function AutoSwitch:HandleAutoSwitchEnabled()
    local currentID = FN.GetActiveNeighborhood and FN.GetActiveNeighborhood() or nil
    if currentID then
        return self:TrySwitch(currentID)
    end
    return false
end

function AutoSwitch:HandleFavoriteChanged(currentID)
    if not currentID and FN.GetActiveNeighborhood then
        currentID = FN.GetActiveNeighborhood()
    end
    if currentID then
        return self:TrySwitch(currentID)
    end
    return false
end

function AutoSwitch:HandleNeighborhoodListChanged()
    local currentID = FN.GetActiveNeighborhood and FN.GetActiveNeighborhood() or nil
    if currentID then
        return self:TrySwitch(currentID)
    end
    return false
end

function AutoSwitch:HandleProgressUpdated(currentID)
    local activeID = FN.GetActiveNeighborhood and FN.GetActiveNeighborhood() or currentID
    if not activeID then return false end
    local sid = tostring(activeID)
    local isComplete = self:IsComplete(sid)
    local wasComplete = lastSeenCompleteState[sid]
    lastSeenCompleteState[sid] = isComplete and true or false

    local preferred = self:EvaluateTarget(sid)
    if preferred and tostring(preferred) ~= sid then
        return self:TrySwitch(sid, nil, { ignoreCooldown = true })
    end

    if isComplete and not wasComplete then
        return self:TrySwitch(sid, nil, { ignoreCooldown = true })
    end
    return false
end

function AutoSwitch:GetLastSwitchInfo()
    return lastSwitchFrom, lastSwitchTo, lastSwitchAt
end
