-- ============================================================================
-- Vamoose's Companion - Store
-- Redux-lite state management with reducers and persistence
-- ============================================================================

VC = VC or {}

-- Default state template
local DEFAULT_STATE = {
    config = {
        debug = false,
        theme = "slipstreamdark",
        portraitSize = "standard",  -- "compact" (48x48) or "standard" (80x100)
        fontFamily = "ARIALN",
        fontScale = 0,
        activeCompanion = "scurry",
        randomCompanion = false,
        portraitScale = 1.0,
        portraitFrame = "atlas",      -- "atlas" (barbershop) or "ornate" (nanobanana)
        modelOverrides = { scurry = 42744 },  -- default Scurry to 4th variant
        chattiness = 3,                -- index into CHATTINESS (1=minimal, 4=maximum)
        muted = false,
        displayModes = { bubble = true, chat = false }, -- both can be active
        eventToggles = {
            farming = true,
            gold = true,
            achieve = true,
            death = true,
            ambient = true,
            quest = true,
            boss = true,
            levelup = true,
            rep = true,
            zone = true,
            login = true,
            housing = true,
            mythicplus = true,
            petbattle = true,
            group = true,
            crafting = true,
            resurrection = true,
            battlerez = true,
            travelerslog = true,
            scenario = true,
            transmog = true,
            appearance = true,
            transmogcollection = true,
            fishing = true,
            afkreturn = true,
            reprank = true,
            mission = true,
            logout = true,
            craftingorder = true,
            achieveprogress = true,
            rarespawn = true,
            combatend = true,
            friendonline = true,
            almostlevel = true,
        },
    },
    ui = {
        portraitPoint = nil,           -- { point, relTo, relPoint, x, y }
        portraitShown = true,
    },
    quoteHistory = {},                 -- rolling array of rich entries (max HISTORY_MAX)
    bonding = {},                      -- per-companion rapport { [key] = { rapport, level, dailyGains, ... } }
    memory = {                         -- persistent companion memory (Phase 1)
        lifetimeCounts = {
            bossKills = 0,
            deaths = 0,
            questsCompleted = 0,
            achievementsEarned = 0,
            goldEarned = 0,
            zonesVisited = 0,
            mythicPlusCompleted = 0,
            craftingCrits = 0,
            fishCaught = 0,
            sessionCount = 0,
        },
        bossKills = {},                -- ring buffer max 10: { {name, difficultyId, ts} }
        milestonesFired = {},          -- { ["boss_10"] = true }
        streakMilestonesFired = {},    -- { ["streak_7"] = true }
    },
}

-- Deep copy helper
local function DeepCopy(orig)
    local copy
    if type(orig) == "table" then
        copy = {}
        for k, v in pairs(orig) do
            copy[k] = DeepCopy(v)
        end
    else
        copy = orig
    end
    return copy
end

VC.Store = {
    state = DeepCopy(DEFAULT_STATE),
    _defaultState = DEFAULT_STATE,
    reducers = {},
    saveTimer = nil,
}

function VC.Store:ResetToDefaults()
    self.state = DeepCopy(DEFAULT_STATE)
end

function VC.Store:GetState()
    return self.state
end

function VC.Store:RegisterReducer(action, reducerFn)
    self.reducers[action] = reducerFn
end

function VC.Store:Dispatch(action, payload)
    local reducer = self.reducers[action]
    if reducer then
        local newState = reducer(self.state, payload)
        if newState then
            self.state = newState
            VC.EventBus:Trigger("VC_STATE_CHANGED", { action = action, state = self.state })
            self:QueueSave()
        end
    else
        if self.state.config.debug then
            print("|cFFdc322f[VC Store]|r No reducer found for:", action)
        end
    end
end

-- ============================================================================
-- SAVEDVARIABLES PERSISTENCE
-- ============================================================================

function VC.Store:LoadFromSavedVariables()
    if not VC_DB then
        VC_DB = {}
    end

    -- Restore config (merge to preserve new defaults)
    if VC_DB.config then
        for key, value in pairs(VC_DB.config) do
            if key == "eventToggles" and type(value) == "table" then
                for toggleKey, toggleVal in pairs(value) do
                    self.state.config.eventToggles[toggleKey] = toggleVal
                end
            elseif key == "portraitOverrides" then
                -- dead field, skip (removed in ModelScene migration)
            elseif key == "modelOverrides" and type(value) == "table" then
                self.state.config[key] = DeepCopy(value)
            else
                self.state.config[key] = value
            end
        end
    end

    -- Migrate old displayMode string -> displayModes table
    if VC_DB.config and VC_DB.config.displayMode and not VC_DB.config.displayModes then
        local old = VC_DB.config.displayMode
        self.state.config.displayModes = { bubble = (old == "bubble" or old == nil), chat = (old == "chat") }
        VC_DB.config.displayMode = nil
    end

    -- Fallback if saved companion has no loaded quote DB
    local ac = self.state.config.activeCompanion
    if not VC_QuoteDB or not VC_QuoteDB[ac] then
        self.state.config.activeCompanion = "scurry"
    end

    -- Restore UI state
    if VC_DB.ui then
        self.state.ui.portraitPoint = VC_DB.ui.portraitPoint
        if VC_DB.ui.portraitShown ~= nil then
            self.state.ui.portraitShown = VC_DB.ui.portraitShown
        end
    end

    -- Restore quote history (migrate old string-ID format to rich entries)
    if VC_DB.quoteHistory then
        if #VC_DB.quoteHistory > 0 and type(VC_DB.quoteHistory[1]) == "string" then
            self.state.quoteHistory = {} -- wipe old format
        else
            self.state.quoteHistory = VC_DB.quoteHistory
        end
    end

    -- Restore bonding state (DeepCopy to avoid shared reference with SavedVariables)
    if VC_DB.bonding then
        self.state.bonding = DeepCopy(VC_DB.bonding)
    end

    -- Restore memory state (merge to preserve new default keys)
    if VC_DB.memory then
        local saved = VC_DB.memory
        if saved.lifetimeCounts then
            for k, v in pairs(saved.lifetimeCounts) do
                self.state.memory.lifetimeCounts[k] = v
            end
        end
        if saved.bossKills then self.state.memory.bossKills = DeepCopy(saved.bossKills) end
        if saved.milestonesFired then self.state.memory.milestonesFired = DeepCopy(saved.milestonesFired) end
        if saved.streakMilestonesFired then self.state.memory.streakMilestonesFired = DeepCopy(saved.streakMilestonesFired) end
    end

    if self.state.config.debug then
        print("|cFFA0522D[VC Store]|r Loaded state from SavedVariables")
    end
end

function VC.Store:QueueSave()
    if self.saveTimer then self.saveTimer:Cancel() end
    self.saveTimer = C_Timer.NewTimer(1, function()
        if InCombatLockdown() then self:QueueSave(); return end
        self:SaveToSavedVariables()
    end)
end

function VC.Store:SaveToSavedVariables()
    VC_DB = VC_DB or {}

    VC_DB.config = {
        debug = self.state.config.debug,
        theme = self.state.config.theme,
        portraitSize = self.state.config.portraitSize,
        fontFamily = self.state.config.fontFamily,
        fontScale = self.state.config.fontScale,
        activeCompanion = self.state.config.activeCompanion,
        randomCompanion = self.state.config.randomCompanion,
        chattiness = self.state.config.chattiness,
        muted = self.state.config.muted,
        portraitScale = self.state.config.portraitScale,
        portraitFrame = self.state.config.portraitFrame,
        displayModes = DeepCopy(self.state.config.displayModes),
        eventToggles = DeepCopy(self.state.config.eventToggles),
        modelOverrides = DeepCopy(self.state.config.modelOverrides),
    }

    VC_DB.ui = {
        portraitPoint = self.state.ui.portraitPoint,
        portraitShown = self.state.ui.portraitShown,
    }

    VC_DB.quoteHistory = self.state.quoteHistory
    VC_DB.bonding = self.state.bonding
    VC_DB.memory = self.state.memory

    if self.state.config.debug then
        print("|cFFA0522D[VC Store]|r Saved state to SavedVariables")
    end
end

function VC.Store:Flush()
    if self.saveTimer then
        self.saveTimer:Cancel()
        self.saveTimer = nil
    end
    self:SaveToSavedVariables()
end

-- ============================================================================
-- REDUCERS
-- ============================================================================

-- SET_CONFIG: Generic config setter
VC.Store:RegisterReducer("SET_CONFIG", function(state, payload)
    local newState = DeepCopy(state)
    if payload.key then
        newState.config[payload.key] = payload.value
    end
    return newState
end)

-- SET_CHATTINESS: Change chattiness tier (1-4)
VC.Store:RegisterReducer("SET_CHATTINESS", function(state, payload)
    local level = payload.level
    if not level or level < 1 or level > 4 then return state end
    local newState = DeepCopy(state)
    newState.config.chattiness = level
    return newState
end)

-- SET_MUTED: Toggle global mute
VC.Store:RegisterReducer("SET_MUTED", function(state, payload)
    local newState = DeepCopy(state)
    newState.config.muted = payload.muted
    return newState
end)

-- TOGGLE_DISPLAY_MODE: toggle "bubble" or "chat" independently
VC.Store:RegisterReducer("TOGGLE_DISPLAY_MODE", function(state, payload)
    if not payload.mode then return state end
    local newState = DeepCopy(state)
    newState.config.displayModes[payload.mode] = not newState.config.displayModes[payload.mode]
    return newState
end)

-- SET_EVENT_TOGGLE: Toggle specific event type on/off
VC.Store:RegisterReducer("SET_EVENT_TOGGLE", function(state, payload)
    if not payload.eventType then return state end
    local newState = DeepCopy(state)
    newState.config.eventToggles[payload.eventType] = payload.enabled
    return newState
end)

-- ADD_QUOTE_HISTORY: Append rich entry { id, text, companionKey, eventType, quality, timestamp }
VC.Store:RegisterReducer("ADD_QUOTE_HISTORY", function(state, payload)
    local entry = payload.entry
    if not entry then return state end
    local newState = DeepCopy(state)
    table.insert(newState.quoteHistory, entry)
    local max = VC.Constants.HISTORY_MAX
    while #newState.quoteHistory > max do
        table.remove(newState.quoteHistory, 1)
    end
    return newState
end)

-- SET_PORTRAIT_POSITION: Save portrait anchor
VC.Store:RegisterReducer("SET_PORTRAIT_POSITION", function(state, payload)
    local newState = DeepCopy(state)
    newState.ui.portraitPoint = payload.point -- { point, relTo, relPoint, x, y } or nil
    return newState
end)

-- SET_PORTRAIT_SHOWN: Toggle portrait visibility
VC.Store:RegisterReducer("SET_PORTRAIT_SHOWN", function(state, payload)
    local newState = DeepCopy(state)
    newState.ui.portraitShown = payload.shown
    return newState
end)

-- SET_QUOTE_HISTORY: Replace quote history (used by category trim)
VC.Store:RegisterReducer("SET_QUOTE_HISTORY", function(state, payload)
    local newState = DeepCopy(state)
    newState.quoteHistory = payload.history or {}
    return newState
end)

-- SET_MODEL_OVERRIDE: Set custom displayID for a companion
VC.Store:RegisterReducer("SET_MODEL_OVERRIDE", function(state, payload)
    if not payload.companionKey then return state end
    local newState = DeepCopy(state)
    newState.config.modelOverrides[payload.companionKey] = payload.displayID
    return newState
end)

-- CLEAR_HISTORY: Reset quote history
VC.Store:RegisterReducer("CLEAR_HISTORY", function(state)
    local newState = DeepCopy(state)
    newState.quoteHistory = {}
    return newState
end)

-- SET_BONDING_LOGIN: Initialize or update streak/login fields without touching rapport
VC.Store:RegisterReducer("SET_BONDING_LOGIN", function(state, payload)
    local key = payload.companionKey
    if not key then return state end
    local newState = DeepCopy(state)
    if not newState.bonding[key] then
        newState.bonding[key] = { rapport = 0, level = "Stranger", dailyGains = 0, lastDay = nil,
                                  streakDays = 0, lastStreakDay = nil, lastLoginTime = 0 }
    end
    local b = newState.bonding[key]
    if payload.streakDays ~= nil then b.streakDays = payload.streakDays end
    if payload.lastStreakDay ~= nil then b.lastStreakDay = payload.lastStreakDay end
    if payload.lastLoginTime ~= nil then b.lastLoginTime = payload.lastLoginTime end
    return newState
end)

-- ADD_RAPPORT: Add rapport to a companion's bonding state (enforces daily cap)
VC.Store:RegisterReducer("ADD_RAPPORT", function(state, payload)
    local key = payload.companionKey
    if not key then return state end

    local newState = DeepCopy(state)
    local b = newState.bonding[key]
    if not b then
        b = { rapport = 0, level = "Stranger", dailyGains = 0, lastDay = nil,
              streakDays = 0, lastStreakDay = nil, lastLoginTime = 0 }
        newState.bonding[key] = b
    end

    local amount = payload.amount or 0
    local today = date("%Y%m%d")

    -- Reset daily counter if new day
    if b.lastDay ~= today then
        b.lastDay = today
        b.dailyGains = 0
    end

    -- Apply daily cap for positive changes (bypass with ignoreCap)
    if amount > 0 and not payload.ignoreCap then
        local remaining = VC.Constants.BONDING.DAILY_CAP - b.dailyGains
        if remaining <= 0 then return newState end
        amount = math.min(amount, remaining)
        b.dailyGains = b.dailyGains + amount
    end

    b.rapport = math.max(0, b.rapport + amount)

    -- Recalculate level
    local newLevel = "Stranger"
    for _, lvl in ipairs(VC.Constants.BONDING.LEVELS) do
        if b.rapport >= lvl.threshold then newLevel = lvl.name end
    end
    b.level = newLevel

    return newState
end)

-- ============================================================================
-- MEMORY REDUCERS
-- ============================================================================

-- INCREMENT_LIFETIME_COUNT: Increment a lifetime counter by amount (default 1)
VC.Store:RegisterReducer("INCREMENT_LIFETIME_COUNT", function(state, payload)
    if not payload.key then return state end
    local newState = DeepCopy(state)
    local counts = newState.memory.lifetimeCounts
    counts[payload.key] = (counts[payload.key] or 0) + (payload.amount or 1)
    return newState
end)

-- PUSH_BOSS_KILL: Append to boss kill ring buffer (max 10, FIFO)
VC.Store:RegisterReducer("PUSH_BOSS_KILL", function(state, payload)
    if not payload.entry then return state end
    local newState = DeepCopy(state)
    local kills = newState.memory.bossKills
    table.insert(kills, payload.entry)
    while #kills > 10 do
        table.remove(kills, 1)
    end
    return newState
end)

-- SET_MILESTONE_FIRED: Mark a milestone as fired (never repeat)
VC.Store:RegisterReducer("SET_MILESTONE_FIRED", function(state, payload)
    if not payload.key then return state end
    local newState = DeepCopy(state)
    newState.memory.milestonesFired[payload.key] = true
    return newState
end)

-- SET_STREAK_MILESTONE_FIRED: Mark a streak milestone as fired
VC.Store:RegisterReducer("SET_STREAK_MILESTONE_FIRED", function(state, payload)
    if not payload.key then return state end
    local newState = DeepCopy(state)
    newState.memory.streakMilestonesFired[payload.key] = true
    return newState
end)
