local _, NSI = ... -- Internal namespace

NSI.EncounterAlerts = NSI.EncounterAlerts or {}

local ID_CHARS = "abcdefghijklmnopqrstuvwxyz0123456789"
function NSI.EncounterAlerts:GenerateAlertID()
    local id = ""
    for _ = 1, 5 do
        local i = math.random(1, #ID_CHARS)
        id = id .. ID_CHARS:sub(i, i)
    end
    return id
end

function NSI:DefaultLoadConditions()
    return {
        Roles   = {},
        Classes = {},
        SpecIDs = {},
        Names = {},
    }
end

-- Builds a flat alert definition for use with AddEncounterAlert.
-- displayType: "Text", "Bar", "Icon", or "Circle"
-- timers: { [phase] = {times...} }
-- overrides: optional table of field overrides
function NSI:MakeEncounterAlert(data, timers)
    local group = data.group
    if group and type(group) == "table" then
        group = data.phase and group[data.phase]
    end
    local name = data.name
    if name and type(name) == "table" then
        name = data.phase and name[data.phase]
    end
    local isEnabled
    if data.overrides and data.overrides.enabled ~= nil then
        isEnabled = data.overrides.enabled
    end
    local defaultEnabled = isEnabled ~= false
    if isEnabled == nil then
        isEnabled = NSRT.Alerts.ReloeReminders
    end
    local a = {
        internalID     = data.internalID,
        name           = name or data.internalID,
        text           = data.text,
        spellID        = data.spellID,
        customIcon     = data.customIcon,
        TTS            = data.TTS,
        TTSTimer       = data.TTSTimer or data.dur,
        dur            = data.dur,
        timers         = timers or data.timers or {},
        phase          = data.phase,
        DisplayType    = data.DisplayType,
        HideTimer      = data.HideTimer,
        IsAlert        = true,
        ReloeReminder  = true,
        enabled        = isEnabled,
        DefaultEnabled = defaultEnabled,
        extraOptions   = data.extraOptions,
        Preview        = data.Preview,
        isSpecialDisplay = data.isSpecialDisplay,
        isConditional  = data.isConditional,
        Version        = data.Version,
        sticky         = data.sticky or 0,
        group          = group,
    }
    if data.overrides then
        for k, v in pairs(data.overrides) do a[k] = v end
    end
    return a
end

function NSI:UniqueAlertID(diffTable, ReloeReminder, internalID)
    local id = internalID
    if ReloeReminder and not id then
        print("No internalID found for Reloe reminder alert. Aborting")
        return
    elseif not id then
        repeat id = NSI.EncounterAlerts:GenerateAlertID() until not diffTable[id]
    end
    return id
end

local function GetAlertVersionNumber(version)
    return version and type(version) == "table" and version.versionNumber or version
end

local function ShouldApplyVersionUpdate(existing, alertDef)
    local newVersion = GetAlertVersionNumber(alertDef and alertDef.Version)
    if not newVersion then return false end

    local oldVersion = GetAlertVersionNumber(existing and existing.Version)
    return not oldVersion or newVersion > oldVersion
end

local function ApplyVersionFieldUpdates(existing, alertDef)
    local version = alertDef and alertDef.Version

    for key, value in pairs(version) do
        if key ~= "versionNumber" then
            existing[key] = value
        end
    end
end

function NSI:GetEncounterAlertID(encID)
    self.EncounterAlertID = self.EncounterAlertID or {}
    if not self.EncounterAlertID[encID] then
        self.EncounterAlertID[encID] = 0
    end
    self.EncounterAlertID[encID] = self.EncounterAlertID[encID] + 1
    return self.EncounterAlertID[encID]
end

function NSI:AddEncounterAlert(data)
    self.EncounterAlertID = self.EncounterAlertID or {}
    if data.difficulties and not data.timers then -- special case for alerts that don't use the actual reminder system but have their own display
        for _, diff in ipairs(data.difficulties) do
            local alertDef = self:MakeEncounterAlert(data)
            alertDef.id = data.id or self:GetEncounterAlertID(data.encID)
            self:InsertEncounterAlert(data.encID, diff, alertDef, true)
        end
        return
    end
    for diffID, phaseData in pairs(data.timers or {}) do
        if phaseData[1] and type(phaseData[1]) == "table" then -- multiple phases were provided
            for phase, timers in ipairs(phaseData) do
                if next(timers) then
                    data.phase = phase
                    local alertDef = self:MakeEncounterAlert(data, timers)
                    alertDef.internalID = data.internalID.."_P"..phase
                    alertDef.id = data.id or self:GetEncounterAlertID(data.encID)
                    self:InsertEncounterAlert(data.encID, diffID, alertDef, true)
                end
            end
        else
            local timers = phaseData
            local alertDef = self:MakeEncounterAlert(data, timers)
            alertDef.id = data.id or self:GetEncounterAlertID(data.encID)
            self:InsertEncounterAlert(data.encID, diffID, alertDef, true)
        end
    end
end

-- Adds or updates an alert entry keyed by a short unique ID at NSRT.EncounterAlerts[encId][diffID].
-- `name` is the human-readable display name stored in alertDef.name and used for lookup.
-- to preserve user-modified settings (enabled, TTS overrides, etc.).
function NSI:InsertEncounterAlert(encId, diffID, alertDef, ReloeReminder)
    NSRT.EncounterAlerts[encId]         = NSRT.EncounterAlerts[encId] or {}
    NSRT.EncounterAlerts[encId][diffID] = NSRT.EncounterAlerts[encId][diffID] or {}
    local diffTable = NSRT.EncounterAlerts[encId][diffID]
    local existing = diffTable[alertDef.internalID]
    local VersionUpdate = existing and ShouldApplyVersionUpdate(existing, alertDef)
    local FullOverwrite = existing and existing.Reset
    local applyDefaultEnabled = self._ApplyReloeAutoEnable or (NSRT.Alerts and NSRT.Alerts.ReloeReminders)
    if FullOverwrite then
        if applyDefaultEnabled and not existing.UserModifiedEnabled then
            alertDef.enabled = alertDef.DefaultEnabled ~= false
        else
            alertDef.enabled = existing.enabled
        end
        alertDef.UserModifiedEnabled = existing.UserModifiedEnabled
        diffTable[self:UniqueAlertID(diffTable, ReloeReminder, alertDef.internalID)] = alertDef
        return
    elseif existing then
        existing.timers = alertDef.timers
        existing.id = alertDef.id
        existing.isConditional = alertDef.isConditional
        existing.extraOptions = alertDef.extraOptions
        existing.Preview = alertDef.Preview
        existing.phase = alertDef.phase
        existing.isSpecialDisplay = alertDef.isSpecialDisplay
        existing.DefaultEnabled = alertDef.DefaultEnabled
        existing.BlockCopy = alertDef.BlockCopy
        existing.Version = alertDef.Version
        if applyDefaultEnabled and not existing.UserModifiedEnabled then
            existing.enabled = alertDef.DefaultEnabled ~= false
        end
        if VersionUpdate then
            ApplyVersionFieldUpdates(existing, alertDef)
        end
        return
    end
    diffTable[self:UniqueAlertID(diffTable, ReloeReminder, alertDef.internalID)] = alertDef
end

-- Returns the alert entry for the given encId/diffID whose name matches, or nil.
function NSI:GetEncounterAlertByName(encId, diffID, name)
    local diffTable = NSRT.EncounterAlerts[encId] and NSRT.EncounterAlerts[encId][diffID]
    if not diffTable then return nil end
    for _, entry in pairs(diffTable) do
        if type(entry) == "table" and entry.name == name then return entry end
    end
end

function NSI:RemoveEncounterAlert(encID, diffID, internalID)
    if NSRT.EncounterAlerts and NSRT.EncounterAlerts[encID] and NSRT.EncounterAlerts[encID][diffID] then
        NSRT.EncounterAlerts[encID][diffID][internalID] = nil
    end
end
