require "MS/MS_StatusIndicator"
require "MS/MS_NaturesCallCompat"

local isInitialized = false
local INDICATOR_TYPES = {
    "ISNCBladderStatusIndicator",
    "ISNCBowelStatusIndicator",
    "ISNCGutHealthStatusIndicator",
    "ISNCSoilageStatusIndicator",
    "ISNCDigestiveEffectsStatusIndicator",
}

local DEFAULT_COLORS = {
    -- Store colors as normalized RGB tables to avoid runtime hex parsing.
    ISNCBladderStatusIndicator = { r = 1.000000, g = 0.850980, b = 0.000000 }, -- #FFD900
    ISNCBowelStatusIndicator = { r = 0.796078, g = 0.505882, b = 0.200000 }, -- #CB8133
    ISNCGutHealthStatusIndicator = { r = 0.350000, g = 0.780000, b = 0.450000 },
    ISNCSoilageStatusIndicator = { r = 0.937255, g = 0.576471, b = 0.364706 }, -- #EF935D
    ISNCDigestiveEffectsStatusIndicator = { r = 0.900000, g = 0.520000, b = 0.220000 },
}

local OLD_DEFAULT_COLORS = {
    -- Beta 1-3 Nature's Call default colors. Used only to migrate unchanged
    -- saved configs without overriding user-customized colors.
    ISNCBladderStatusIndicator = { r = 0.420000, g = 0.620000, b = 0.950000 },
    ISNCBowelStatusIndicator = { r = 0.580000, g = 0.420000, b = 0.280000 },
    ISNCSoilageStatusIndicator = { r = 0.650000, g = 0.480000, b = 0.320000 },
}

local function colorsAreClose(a, b)
    if not a or not b then return false end
    local dr = math.abs((tonumber(a.r) or 0) - (tonumber(b.r) or 0))
    local dg = math.abs((tonumber(a.g) or 0) - (tonumber(b.g) or 0))
    local db = math.abs((tonumber(a.b) or 0) - (tonumber(b.b) or 0))
    return dr < 0.002 and dg < 0.002 and db < 0.002
end

local function applyNaturesCallConfigMigration(config, indicatorType)
    if not config or not indicatorType then return end

    local oldColor = OLD_DEFAULT_COLORS[indicatorType]
    local newColor = DEFAULT_COLORS[indicatorType]
    if oldColor and newColor and colorsAreClose(config.color, oldColor) then
        config.color = { r = newColor.r, g = newColor.g, b = newColor.b }
    end

    if indicatorType == "ISNCDigestiveEffectsStatusIndicator" then
        -- NC Effects has no meaningful fixed thresholds because it can show
        -- several effects with different units. Keep old beta configs clean.
        config.displayThresholds = false
        if tonumber(config.animationThreshold) == 25 then
            config.animationThreshold = 0
        end
    end
end

local function isNaturesCallActive()
    return MS_NaturesCallCompat and MS_NaturesCallCompat.isActive and MS_NaturesCallCompat.isActive() == true
end


local function translateNC(key, fallback)
    local value = getText(key)
    if value == nil or value == key or value == "" then
        return fallback or key
    end
    return tostring(value)
end

local function formatDigestiveEffectValue(effect)
    if not effect then return "" end

    -- NC Effects is a composite active-effects indicator. The bar is binary
    -- active/inactive, so the visible text should show the source unit instead
    -- of mixing an arbitrary severity percentage with hours or intensity.
    local value = tonumber(effect.value) or 0
    if effect.valueType == "hours" then
        return string.format("%.1fh", value)
    elseif effect.valueType == "strain" then
        return string.format("%.2f", value)
    elseif effect.valueType == "intensity" then
        return string.format("%.1f", value)
    end
    return ""
end

local function addIndicatorToWidget(playerIndex, player, indicatorType, classRef)
    if not StatusWidget.indicators[playerIndex] then
        StatusWidget.indicators[playerIndex] = {}
    end
    if StatusWidget.indicators[playerIndex][indicatorType] then
        return
    end

    local config = MSConfig.getIndicatorConfig(playerIndex, indicatorType)
    applyNaturesCallConfigMigration(config, indicatorType)
    local width, height
    if config.style == "bar" then
        width = config.barSize.width
        height = config.barSize.height
    else
        width = config.circularSize
        height = config.circularSize
    end

    local indicator = classRef:new(config.position.x, config.position.y, width, height, player)
    indicator:initialise()
    indicator:addToUIManager()
    StatusWidget.indicators[playerIndex][indicatorType] = indicator
    if StatusWidget.indicatorsByType and StatusWidget.indicatorsByType[playerIndex] then
        StatusWidget.indicatorsByType[playerIndex][indicatorType] = indicator
    end
end

-- ---------------------------------------------------------------------------------------- --
-- Nature's Call Bladder
-- ---------------------------------------------------------------------------------------- --
ISNCBladderStatusIndicator = MS_StatusIndicator:derive("ISNCBladderStatusIndicator")

function ISNCBladderStatusIndicator:new(x, y, width, height, player)
    local o = MS_StatusIndicator.new(self, x, y, width, height, player)
    o.baseIconName = "NCBladder"
    o.preferModernStatusColorIcon = true
    o.__type = "ISNCBladderStatusIndicator"
    o.indicatorColor = DEFAULT_COLORS.ISNCBladderStatusIndicator
    return o
end

function ISNCBladderStatusIndicator:getValue()
    if not self.player or self.player:isDead() then return 0 end
    local value = MS_NaturesCallCompat.getBladderFill(self.player, false)
    if (tonumber(value) or 0) < 0 then return 0 end
    return tonumber(value) or 0
end

function ISNCBladderStatusIndicator:getHoverText(value)
    if not self.player or self.player:isDead() then return "0" end
    local fill = MS_NaturesCallCompat.getBladderFill(self.player, true)
    if (tonumber(fill) or 0) < 0 then
        return getText("IGUI_ModernStatus_NCMeterHidden")
    end
    return MS_NaturesCallCompat.formatFill(fill) or "0"
end

-- ---------------------------------------------------------------------------------------- --
-- Nature's Call Bowel
-- ---------------------------------------------------------------------------------------- --
ISNCBowelStatusIndicator = MS_StatusIndicator:derive("ISNCBowelStatusIndicator")

function ISNCBowelStatusIndicator:new(x, y, width, height, player)
    local o = MS_StatusIndicator.new(self, x, y, width, height, player)
    o.baseIconName = "NCBowel"
    o.preferModernStatusColorIcon = true
    o.__type = "ISNCBowelStatusIndicator"
    o.indicatorColor = DEFAULT_COLORS.ISNCBowelStatusIndicator
    return o
end

function ISNCBowelStatusIndicator:getValue()
    if not self.player or self.player:isDead() then return 0 end
    local value = MS_NaturesCallCompat.getBowelFill(self.player, false)
    if (tonumber(value) or 0) < 0 then return 0 end
    return tonumber(value) or 0
end

function ISNCBowelStatusIndicator:getHoverText(value)
    if not self.player or self.player:isDead() then return "0" end
    local fill = MS_NaturesCallCompat.getBowelFill(self.player, true)
    if (tonumber(fill) or 0) < 0 then
        return getText("IGUI_ModernStatus_NCMeterHidden")
    end
    return MS_NaturesCallCompat.formatFill(fill) or "0"
end

-- ---------------------------------------------------------------------------------------- --
-- Nature's Call Gut Health
-- ---------------------------------------------------------------------------------------- --
ISNCGutHealthStatusIndicator = MS_StatusIndicator:derive("ISNCGutHealthStatusIndicator")

function ISNCGutHealthStatusIndicator:new(x, y, width, height, player)
    local o = MS_StatusIndicator.new(self, x, y, width, height, player)
    o.baseIconName = "NCGut"
    o.preferModernStatusColorIcon = true
    o.__type = "ISNCGutHealthStatusIndicator"
    o.indicatorColor = DEFAULT_COLORS.ISNCGutHealthStatusIndicator
    return o
end

function ISNCGutHealthStatusIndicator:getValue()
    if not self.player or self.player:isDead() then return MS_NaturesCallCompat.getGutNeutral() end
    return MS_NaturesCallCompat.getGutHealth(self.player, false)
end

function ISNCGutHealthStatusIndicator:getHoverText(value)
    local gut = tonumber(value) or MS_NaturesCallCompat.getGutHealth(self.player, false)
    return string.format("%.1f / %.0f", gut, MS_NaturesCallCompat.getGutMax())
end

-- ---------------------------------------------------------------------------------------- --
-- Nature's Call Soilage
-- ---------------------------------------------------------------------------------------- --
ISNCSoilageStatusIndicator = MS_StatusIndicator:derive("ISNCSoilageStatusIndicator")

function ISNCSoilageStatusIndicator:new(x, y, width, height, player)
    local o = MS_StatusIndicator.new(self, x, y, width, height, player)
    o.baseIconName = "NCSoilage"
    o.preferModernStatusColorIcon = true
    o.__type = "ISNCSoilageStatusIndicator"
    o.indicatorColor = DEFAULT_COLORS.ISNCSoilageStatusIndicator
    return o
end

function ISNCSoilageStatusIndicator:getValue()
    if not self.player or self.player:isDead() then return 0 end
    local data = MS_NaturesCallCompat.getSoilageData(self.player)
    return tonumber(data and data.value) or 0
end

function ISNCSoilageStatusIndicator:getHoverText(value)
    return string.format("%.0f%%", tonumber(value) or 0)
end


-- ---------------------------------------------------------------------------------------- --
-- Nature's Call Digestive Effects
-- ---------------------------------------------------------------------------------------- --
ISNCDigestiveEffectsStatusIndicator = MS_StatusIndicator:derive("ISNCDigestiveEffectsStatusIndicator")

function ISNCDigestiveEffectsStatusIndicator:new(x, y, width, height, player)
    local o = MS_StatusIndicator.new(self, x, y, width, height, player)
    o.baseIconName = "NCDigestiveEffects"
    o.preferModernStatusColorIcon = true
    o.__type = "ISNCDigestiveEffectsStatusIndicator"
    o.indicatorColor = DEFAULT_COLORS.ISNCDigestiveEffectsStatusIndicator
    return o
end

function ISNCDigestiveEffectsStatusIndicator:getValue()
    if not self.player or self.player:isDead() then return 0 end
    local data = MS_NaturesCallCompat.getDigestiveEffectsData(self.player)
    -- Use a normalized 0..1 visual value. Timed effects use 4 h as 100%,
    -- while intensity/strain effects are normalized by the compat layer.
    return data and ((tonumber(data.value) or 0) / 100) or 0
end

function ISNCDigestiveEffectsStatusIndicator:getHoverText(value)
    if not self.player or self.player:isDead() then return getText("IGUI_ModernStatus_None") end
    local data = MS_NaturesCallCompat.getDigestiveEffectsData(self.player)
    if not data or (tonumber(data.activeCount) or 0) <= 0 or not data.primary then
        return getText("IGUI_ModernStatus_None")
    end

    local primary = data.primary
    local text = translateNC(primary.labelKey, primary.fallback) .. " " .. formatDigestiveEffectValue(primary)
    local extra = (tonumber(data.activeCount) or 1) - 1
    if extra > 0 then
        text = text .. " +" .. tostring(extra)
    end
    return text
end

-- ---------------------------------------------------------------------------------------- --
-- Integrate to Modern Status
-- ---------------------------------------------------------------------------------------- --
local function addDefaultConfig(config, indicatorType)
    if not config.indicators[indicatorType] then
        config.indicators[indicatorType] = MSConfig.getDefaultIndicatorConfig(indicatorType)
    end
    if DEFAULT_COLORS[indicatorType] then
        config.indicators[indicatorType].color = DEFAULT_COLORS[indicatorType]
    end
    config.indicators[indicatorType].autoHide = true
    applyNaturesCallConfigMigration(config.indicators[indicatorType], indicatorType)
    return config.indicators[indicatorType]
end

local function integrateToModernStatus()
    if isInitialized then return end
    if not isNaturesCallActive() then return end

    MS_IndicatorSettingsPanel.IndicatorNames["ISNCBladderStatusIndicator"] = "IGUI_ModernStatus_NCBladder"
    MS_IndicatorSettingsPanel.IndicatorNames["ISNCBowelStatusIndicator"] = "IGUI_ModernStatus_NCBowel"
    MS_IndicatorSettingsPanel.IndicatorNames["ISNCGutHealthStatusIndicator"] = "IGUI_ModernStatus_NCGutHealth"
    MS_IndicatorSettingsPanel.IndicatorNames["ISNCSoilageStatusIndicator"] = "IGUI_ModernStatus_NCSoilage"
    MS_IndicatorSettingsPanel.IndicatorNames["ISNCDigestiveEffectsStatusIndicator"] = "IGUI_ModernStatus_NCDigestiveEffects"

    local oldCreateUI = StatusWidget.createUI
    StatusWidget.createUI = function(playerIndex, player)
        oldCreateUI(playerIndex, player)
        if not isNaturesCallActive() then return end

        addIndicatorToWidget(playerIndex, player, "ISNCBladderStatusIndicator", ISNCBladderStatusIndicator)
        addIndicatorToWidget(playerIndex, player, "ISNCBowelStatusIndicator", ISNCBowelStatusIndicator)
        addIndicatorToWidget(playerIndex, player, "ISNCGutHealthStatusIndicator", ISNCGutHealthStatusIndicator)
        addIndicatorToWidget(playerIndex, player, "ISNCSoilageStatusIndicator", ISNCSoilageStatusIndicator)
        addIndicatorToWidget(playerIndex, player, "ISNCDigestiveEffectsStatusIndicator", ISNCDigestiveEffectsStatusIndicator)
    end

    if not MSConfig._originalCreateDefaultConfig_NaturesCall then
        MSConfig._originalCreateDefaultConfig_NaturesCall = MSConfig.createDefaultConfig
        MSConfig.createDefaultConfig = function(playerNum)
            local config = MSConfig._originalCreateDefaultConfig_NaturesCall(playerNum)
            if isNaturesCallActive() then
                addDefaultConfig(config, "ISNCBladderStatusIndicator").animationThreshold = 95
                addDefaultConfig(config, "ISNCBowelStatusIndicator").animationThreshold = 95
                addDefaultConfig(config, "ISNCGutHealthStatusIndicator").animationThreshold = 2
                addDefaultConfig(config, "ISNCGutHealthStatusIndicator").autoHideThreshold = 0.5
                addDefaultConfig(config, "ISNCSoilageStatusIndicator").animationThreshold = 25
                local effectsConfig = addDefaultConfig(config, "ISNCDigestiveEffectsStatusIndicator")
                effectsConfig.animationThreshold = 0
                effectsConfig.displayThresholds = false
            end
            return config
        end
    end

    isInitialized = true
end

Events.OnGameBoot.Add(integrateToModernStatus)

return {
    isActive = isNaturesCallActive,
}
