-- ══════════════════════════════════════════════════════════════════════════════
-- SURVIVALSTREAK.LUA — Überlebens-Streak-System
-- Modul: SurvivalStreak | Interface: 11508
--
-- Features:
--   • Aktueller Streak pro Charakter (Zeit seit letztem Tod / erstem Login)
--   • Gilden-Bestenliste: "Wer lebt am längsten?"
--   • Meilenstein-Warnungen: "Du lebst seit 7 Tagen — pass auf!"
--   • Buchseite: Kapitel "Die Ausdauernden"
--   • Sync mit anderen GDL-Nutzern via GDL-Data-Kanal
-- ══════════════════════════════════════════════════════════════════════════════

local addonName, addon = ...
local GDL = _G["GuildDeathLog"]

local UnitName   = UnitName
local UnitGUID   = UnitGUID
local UnitClass  = UnitClass
local IsInGuild  = IsInGuild
local time       = time
local math_floor = math.floor

-- ── Modul ─────────────────────────────────────────────────────────────────────
local SurvivalStreak = {}
GDL:RegisterModule("SurvivalStreak", SurvivalStreak)

-- ── Streak-Meilensteine (in Tagen) ───────────────────────────────────────────
local STREAK_MILESTONES = {
    { days = 1,   label = "Einen Tag überlebt",      color = "|cff1A5A24" },
    { days = 3,   label = "Drei Tage überlebt",      color = "|cff1A5A24" },
    { days = 7,   label = "Eine Woche überlebt",     color = "|cff7A5510" },
    { days = 14,  label = "Zwei Wochen überlebt",    color = "|cff7A5510" },
    { days = 30,  label = "Einen Monat überlebt",    color = "|cff7A3A08" },
    { days = 60,  label = "Zwei Monate überlebt",    color = "|cffFF4500" },
    { days = 100, label = "100 Tage Überlebt",       color = "|cffFF0000" },
}

-- ── Hilfsfunktionen ───────────────────────────────────────────────────────────
local function FormatStreak(seconds)
    if not seconds or seconds < 0 then return "Neu" end
    local days    = math_floor(seconds / 86400)
    local hours   = math_floor((seconds % 86400) / 3600)
    local minutes = math_floor((seconds % 3600) / 60)

    if days >= 30 then
        return string.format("%dd", days)
    elseif days > 0 then
        return string.format("%dd %dh", days, hours)
    elseif hours > 0 then
        return string.format("%dh %dm", hours, minutes)
    else
        return string.format("%dm", minutes)
    end
end

local function GetCharKey()
    local name = UnitName("player")
    local guid = UnitGUID("player")
    -- GUID-basiert (HC-sicher: gleicher Name nach Neuerstellung = anderer GUID)
    return guid and (name .. ":" .. guid:sub(-8)) or name
end

-- ── Streak berechnen ──────────────────────────────────────────────────────────
function SurvivalStreak:GetCurrentStreak(charKey)
    charKey = charKey or GetCharKey()
    local db = GuildDeathLogDB
    if not (db and db.streaks) then return 0, 0 end

    local entry = db.streaks[charKey]
    if not entry then return 0, 0 end

    local streakStart = entry.streakStart or entry.firstSeen or time()
    local elapsed     = time() - streakStart
    local days        = math_floor(elapsed / 86400)

    return elapsed, days
end

-- ── Streak starten/resetten ───────────────────────────────────────────────────
function SurvivalStreak:StartStreak(charKey)
    charKey = charKey or GetCharKey()
    local db = GuildDeathLogDB
    if not db then return end
    if not db.streaks then db.streaks = {} end

    local now = time()
    local existing = db.streaks[charKey]

    if not existing then
        db.streaks[charKey] = {
            name        = UnitName("player"),
            charKey     = charKey,
            streakStart = now,
            firstSeen   = now,
            bestStreak  = 0,
            totalDeaths = 0,
        }
    end

    -- Beim Login: Streak läuft weiter (nicht neu starten)
    -- Nur bei Tod: ResetsStreak aufrufen
end

function SurvivalStreak:ResetStreak(charKey, deathData)
    charKey = charKey or GetCharKey()
    local db = GuildDeathLogDB
    if not (db and db.streaks) then return end
    if not db.streaks[charKey] then return end

    local entry   = db.streaks[charKey]
    local elapsed = time() - (entry.streakStart or time())

    -- Besten Streak speichern
    if elapsed > (entry.bestStreak or 0) then
        entry.bestStreak = elapsed
    end

    entry.totalDeaths  = (entry.totalDeaths or 0) + 1
    entry.streakStart  = time()  -- Neuer Start sofort nach Tod
    entry.lastDeath    = time()
    entry.lastDeathBy  = deathData and deathData.killer or "Unbekannt"
    entry.lastDeathZone = deathData and deathData.zone or "?"

    db.streaks[charKey] = entry
end

-- ── Gilden-Bestenliste ────────────────────────────────────────────────────────
function SurvivalStreak:GetGuildLeaderboard(limit)
    limit = limit or 10
    local db = GuildDeathLogDB
    if not (db and db.streaks) then return {} end

    local entries = {}
    local now = time()

    for key, data in pairs(db.streaks) do
        if data.name and data.streakStart then
            local elapsed = now - data.streakStart
            table.insert(entries, {
                name        = data.name,
                charKey     = key,
                elapsed     = elapsed,
                days        = math_floor(elapsed / 86400),
                formatted   = FormatStreak(elapsed),
                bestStreak  = data.bestStreak or 0,
                totalDeaths = data.totalDeaths or 0,
                lastDeath   = data.lastDeath,
            })
        end
    end

    -- Nach aktueller Streak-Länge sortieren (längste zuerst)
    table.sort(entries, function(a, b)
        return (a.elapsed or 0) > (b.elapsed or 0)
    end)

    -- Top N zurückgeben
    local result = {}
    for i = 1, math.min(limit, #entries) do
        result[i] = entries[i]
    end
    return result
end

-- ── Meilenstein-Check & Warnung ───────────────────────────────────────────────
local _lastMilestoneCheck = 0
local _announcedMilestones = {}

function SurvivalStreak:CheckMilestones()
    local now = time()
    if (now - _lastMilestoneCheck) < 3600 then return end  -- Max 1x pro Stunde
    _lastMilestoneCheck = now

    local elapsed, days = SurvivalStreak:GetCurrentStreak()
    local charKey = GetCharKey()
    -- Persistent statt modul-lokal: sonst poppt der Toast bei JEDEM Login erneut.
    GuildDeathLogDB.streakAnnounced = GuildDeathLogDB.streakAnnounced or {}
    GuildDeathLogDB.streakAnnounced[charKey] = GuildDeathLogDB.streakAnnounced[charKey] or {}
    local announced = GuildDeathLogDB.streakAnnounced[charKey]

    for _, milestone in ipairs(STREAK_MILESTONES) do
        if days >= milestone.days and not announced[milestone.days] then
            announced[milestone.days] = true

            -- Toast-Benachrichtigung
            local UI = GDL:GetModule("UI")
            if UI and UI.ShowMilestoneToast then
                UI:ShowMilestoneToast({
                    name     = milestone.label,
                    desc     = string.format("Aktueller Streak: %s", FormatStreak(elapsed)),
                    icon     = "Interface\\Icons\\Spell_Nature_TimeStop",
                    category = "survival",
                })
            end

            -- Chat-Ausgabe
            GDL:Print(string.format(
                "%s|cff7A5510 Meilenstein:|r %s |cff4A3A28(Streak: %s)|r",
                milestone.color, milestone.label, FormatStreak(elapsed)
            ))
        end
    end
end

-- ── UI-Rendering für Buchseite ────────────────────────────────────────────────
-- Wird von UIChapters aufgerufen für das Kapitel "Die Ausdauernden"
function SurvivalStreak:RenderBookPage(scrollChild, yOffset)
    local db  = GuildDeathLogDB
    local now = time()

    -- ── Eigenen Streak ermitteln ──────────────────────────────────────────────
    local charKey   = GetCharKey()
    local selfEntry = db and db.streaks and db.streaks[charKey]

    -- PlayedTime-Fallback: Wenn 0 Tode → /played als Streak verwenden
    local elapsed = 0
    if selfEntry then
        elapsed = now - (selfEntry.streakStart or now)
    end
    local totalDeaths = selfEntry and (selfEntry.totalDeaths or 0) or 0
    if totalDeaths == 0 then
        local PT = GDL:GetModule("PlayedTime")
        if PT then
            local pt = PT:Get()
            if pt and pt > elapsed then
                elapsed = pt
            end
        end
    end
    local bestElapsed = selfEntry and (selfEntry.bestStreak or 0) or 0

    -- ── SECTION: Meine Überlebensdauer ───────────────────────────────────────
    local selfTitle = scrollChild:CreateFontString(nil, "OVERLAY")
    selfTitle:SetFont("Fonts\\MORPHEUS.TTF", 15, "")
    selfTitle:SetTextColor(0.40, 0.28, 0.14)
    selfTitle:SetShadowOffset(0, 0)
    selfTitle:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 20, yOffset)
    selfTitle:SetText("Meine Ueberlebensdauer")
    yOffset = yOffset - 24

    -- Stat-Zeile: drei Blöcke mit eigenen FontStrings (keine format-Ausrichtung)
    local COL = { 20, 130, 230 }  -- x-Positionen der 3 Blöcke

    local function MakeStat(label, value, valueColor, xPos)
        local lbl = scrollChild:CreateFontString(nil, "OVERLAY")
        lbl:SetFont("Fonts\\FRIZQT__.TTF", 10, "")
        lbl:SetTextColor(0.45, 0.35, 0.22)
        lbl:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", xPos, yOffset)
        lbl:SetText(label .. ":")

        local val = scrollChild:CreateFontString(nil, "OVERLAY")
        val:SetFont("Fonts\\FRIZQT__.TTF", 10, "")
        local r, g, b = unpack(valueColor)
        val:SetTextColor(r, g, b)
        val:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", xPos + 58, yOffset)
        val:SetText(value)
    end

    MakeStat("Aktuell",  FormatStreak(elapsed),   {0.20, 0.72, 0.30}, COL[1])
    MakeStat("Bester",   FormatStreak(bestElapsed), {0.72, 0.58, 0.15}, COL[2])
    MakeStat("Tode",
        totalDeaths > 0 and ("|cffAA3333" .. totalDeaths .. "|r") or "|cff444444" .. totalDeaths .. "|r",
        {1,1,1}, COL[3])
    yOffset = yOffset - 18

    -- Letzter Tod
    if selfEntry and selfEntry.lastDeath then
        local ago = now - selfEntry.lastDeath
        local deathInfo = scrollChild:CreateFontString(nil, "OVERLAY")
        deathInfo:SetFont("Fonts\\FRIZQT__.TTF", 9, "")
        deathInfo:SetTextColor(0.45, 0.35, 0.25)
        deathInfo:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 20, yOffset)
        deathInfo:SetText(string.format("Letzter Tod: vor %s  —  %s (%s)",
            FormatStreak(ago),
            selfEntry.lastDeathBy or "?",
            selfEntry.lastDeathZone or "?"))
        yOffset = yOffset - 16
    end

    yOffset = yOffset - 10

    -- Trennlinie
    local div = scrollChild:CreateTexture(nil, "ARTWORK")
    div:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 15, yOffset)
    div:SetSize(300, 1)
    div:SetColorTexture(0.45, 0.35, 0.20, 0.35)
    yOffset = yOffset - 14

    -- ── SECTION: Gilden-Bestenliste ──────────────────────────────────────────
    local lbTitle = scrollChild:CreateFontString(nil, "OVERLAY")
    lbTitle:SetFont("Fonts\\MORPHEUS.TTF", 14, "")
    lbTitle:SetTextColor(0.40, 0.28, 0.14)
    lbTitle:SetShadowOffset(0, 0)
    lbTitle:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 20, yOffset)
    lbTitle:SetText("Die Ausdauernden der Gilde")
    yOffset = yOffset - 22

    -- Spalten-X-Positionen (fixe Pixel, kein string.format Padding)
    local X_RANK   = 20
    local X_NAME   = 42
    local X_STREAK = 178
    local X_BEST   = 228
    local X_DEATHS = 282

    -- Tabellenkopf
    local function MakeHeader(text, xPos)
        local h = scrollChild:CreateFontString(nil, "OVERLAY")
        h:SetFont("Fonts\\FRIZQT__.TTF", 9, "")
        h:SetTextColor(0.38, 0.30, 0.20)
        h:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", xPos, yOffset)
        h:SetText(text)
    end
    MakeHeader("#",        X_RANK)
    MakeHeader("Charakter",X_NAME)
    MakeHeader("Streak",   X_STREAK)
    MakeHeader("Rekord",   X_BEST)
    MakeHeader("Tode",     X_DEATHS)
    yOffset = yOffset - 14

    -- Trennlinie unter Header
    local hDiv = scrollChild:CreateTexture(nil, "ARTWORK")
    hDiv:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", X_RANK, yOffset + 2)
    hDiv:SetSize(278, 1)
    hDiv:SetColorTexture(0.40, 0.32, 0.18, 0.28)
    yOffset = yOffset - 6

    local leaders = SurvivalStreak:GetGuildLeaderboard(10)

    if #leaders == 0 then
        local empty = scrollChild:CreateFontString(nil, "OVERLAY")
        empty:SetFont("Fonts\\FRIZQT__.TTF", 10, "")
        empty:SetTextColor(0.42, 0.35, 0.25)
        empty:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 20, yOffset)
        empty:SetText("|cff6A5A48Noch keine Daten vorhanden.|r")
        yOffset = yOffset - 18
    else
        for i, entry in ipairs(leaders) do
            local rankColor
            if     i == 1 then rankColor = {0.90, 0.75, 0.10}  -- Gold
            elseif i == 2 then rankColor = {0.72, 0.72, 0.72}  -- Silber
            elseif i == 3 then rankColor = {0.72, 0.44, 0.18}  -- Bronze
            else              rankColor = {0.42, 0.38, 0.30}
            end

            local isMe = (entry.charKey == charKey)

            -- Hintergrund für eigenen Eintrag
            if isMe then
                local rowBg = scrollChild:CreateTexture(nil, "BACKGROUND")
                rowBg:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", X_RANK - 2, yOffset + 1)
                rowBg:SetSize(282, 16)
                rowBg:SetColorTexture(0.20, 0.15, 0.05, 0.22)
            end

            -- Rang
            local rankFS = scrollChild:CreateFontString(nil, "OVERLAY")
            rankFS:SetFont("Fonts\\FRIZQT__.TTF", 10, "")
            rankFS:SetTextColor(unpack(rankColor))
            rankFS:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", X_RANK, yOffset)
            rankFS:SetText(i .. ".")

            -- Name
            local nameFS = scrollChild:CreateFontString(nil, "OVERLAY")
            nameFS:SetFont("Fonts\\FRIZQT__.TTF", 10, "")
            if isMe then
                nameFS:SetTextColor(0.16, 0.36, 0.52)
            else
                nameFS:SetTextColor(0.35, 0.28, 0.18)
            end
            nameFS:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", X_NAME, yOffset)
            nameFS:SetText(entry.name:sub(1, 16))

            -- Aktueller Streak
            local streakFS = scrollChild:CreateFontString(nil, "OVERLAY")
            streakFS:SetFont("Fonts\\FRIZQT__.TTF", 10, "")
            streakFS:SetTextColor(0.22, 0.60, 0.30)
            streakFS:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", X_STREAK, yOffset)
            streakFS:SetText(entry.formatted)

            -- Bester Streak
            local bestFS = scrollChild:CreateFontString(nil, "OVERLAY")
            bestFS:SetFont("Fonts\\FRIZQT__.TTF", 10, "")
            bestFS:SetTextColor(0.60, 0.48, 0.18)
            bestFS:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", X_BEST, yOffset)
            bestFS:SetText(FormatStreak(entry.bestStreak))

            -- Tode
            local deathFS = scrollChild:CreateFontString(nil, "OVERLAY")
            deathFS:SetFont("Fonts\\FRIZQT__.TTF", 10, "")
            deathFS:SetTextColor(entry.totalDeaths > 0 and 0.72 or 0.35,
                                 entry.totalDeaths > 0 and 0.22 or 0.28, 0.20)
            deathFS:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", X_DEATHS, yOffset)
            deathFS:SetText(tostring(entry.totalDeaths))

            yOffset = yOffset - 17
        end
    end

    return yOffset
end

-- ── Tod-Ereignis: Streak resetten ────────────────────────────────────────────
-- Wird von DNLBridge nach HandleDeath aufgerufen
function SurvivalStreak:OnDeath(deathData)
    if not deathData then return end

    if deathData.isSelf then
        local charKey = GetCharKey()
        SurvivalStreak:ResetStreak(charKey, deathData)
        GDL:Print(string.format(
            "|cff7A2010[Streak]|r Dein Streak wurde unterbrochen. Bester: %s",
            FormatStreak((GuildDeathLogDB and GuildDeathLogDB.streaks
                and GuildDeathLogDB.streaks[charKey]
                and GuildDeathLogDB.streaks[charKey].bestStreak) or 0)
        ))
    end

    -- Für Gildenmitglieder: Streak in unserer DB updaten
    -- (kommen via DNL Sync, name ist bekannt)
    if deathData.name and not deathData.isSelf then
        local db = GuildDeathLogDB
        if not (db and db.streaks) then return end

        -- Streak-Eintrag für diesen Charakter finden und resetten
        for key, entry in pairs(db.streaks) do
            local entryName = entry.name or ""
            if entryName == deathData.name then
                local elapsed = time() - (entry.streakStart or time())
                if elapsed > (entry.bestStreak or 0) then
                    entry.bestStreak = elapsed
                end
                entry.totalDeaths  = (entry.totalDeaths or 0) + 1
                entry.streakStart  = time()
                entry.lastDeath    = time()
                entry.lastDeathBy  = deathData.killer
                entry.lastDeathZone = deathData.zone
                break
            end
        end
    end
end

-- ── Initialisierung ───────────────────────────────────────────────────────────
function SurvivalStreak:Initialize()
    if SurvivalStreak._initialized then return end
    SurvivalStreak._initialized = true
    -- SavedVariables vorbereiten
    C_Timer.After(0.5, function()
        local db = GuildDeathLogDB
        if not db then return end
        if not db.streaks then db.streaks = {} end

        -- Eigenen Charakter eintragen/updaten
        SurvivalStreak:StartStreak(GetCharKey())

        -- Meilenstein-Ticker: alle 30 Minuten prüfen
        C_Timer.NewTicker(1800, function()
            SurvivalStreak:CheckMilestones()
        end)

        -- Sofort einmal prüfen (nach Login)
        C_Timer.After(30, function()
            SurvivalStreak:CheckMilestones()
        end)
    end)
end

SurvivalStreak:Initialize()

-- ── Public API ────────────────────────────────────────────────────────────────
SurvivalStreak.FormatStreak = FormatStreak

function SurvivalStreak:PrintStatus()
    local elapsed, days = SurvivalStreak:GetCurrentStreak()
    GDL:Print(string.format(
        "|cff7A5510[Streak]|r Aktuell überlebt: |cff00FF00%s|r (%d Tage)",
        FormatStreak(elapsed), days
    ))
    local leaders = SurvivalStreak:GetGuildLeaderboard(3)
    if #leaders > 0 then
        GDL:Print("|cffFFD100Top 3 der Gilde:|r")
        for i, e in ipairs(leaders) do
            GDL:Print(string.format("  %d. %s — %s", i, e.name, e.formatted))
        end
    end
end
