-- laundromat/cl_ui.lua
-- Interface utilisateur DBALib pour la laverie automatique.
-- Cas 1 : machine libre → MoneyInput + bouton Insérer
-- Cas 2 : machine occupée par moi → ProgressBar + bouton Annuler
-- Cas 3 : machine occupée par autre → Alert (géré dans OpenMenu)
--
-- NOTE : vérification DBALib en lazy (pas d'early-return au top-level)
--        pour respecter l'ordre de chargement GMod.

Laundromat    = Laundromat    or {}
Laundromat.UI = Laundromat.UI or {}

-- ============================================================================
-- State
-- ============================================================================

local CurrentFrame   = nil
local ProgressBarRef = nil
local CurrentMachine = nil
local CurrentDirty   = 0
local IsProgressView = false  -- true si la frame ouverte est la vue "cycle en cours"

-- ============================================================================
-- Helper : DBALib disponible ?
-- ============================================================================

local function dbaLibReady()
    if DBALib and DBALib.UI and DBALib.UI.Frame and DBALib.UI.MoneyInput then
        return true
    end
    ErrorNoHalt("[Laundromat] DBALib v0.8.0+ requis — UI désactivée.\n")
    return false
end

-- ============================================================================
-- Helper : fermer la frame proprement
-- ============================================================================

local function closeFrame()
    hook.Remove("Think", "Laundromat.ProgressTick")

    if IsValid(CurrentFrame) then
        CurrentFrame:Close()
    end
    CurrentFrame   = nil
    ProgressBarRef = nil
    IsProgressView = false
end

-- ============================================================================
-- Laundromat.UI.OpenMenu
-- Appelé depuis cl_net.lua quand le serveur envoie OpenMenu.
-- ============================================================================

function Laundromat.UI.OpenMenu(machine, dirty)
    if not dbaLibReady() then return end

    -- Un seul menu à la fois
    if IsValid(CurrentFrame) then
        closeFrame()
        return
    end

    if not IsValid(machine) then
        Laundromat.UI.OnError("Machine invalide.")
        return
    end

    CurrentMachine = machine
    CurrentDirty   = dirty

    DBALib.Sound("ui_open")

    local S = DBALib.Scale

    -- Lire l'état NW2 de la machine
    local user = machine:GetNW2Entity("laundromat_user")

    -- -----------------------------------------------------------------------
    -- Cas 3 : machine occupée par quelqu'un d'autre
    -- -----------------------------------------------------------------------
    if IsValid(user) and user ~= LocalPlayer() then
        DBALib.UI.Alert({
            title   = "Machine occupée",
            message = "Cette machine est actuellement utilisée par " .. user:Nick() .. ".",
            onClose = function() end,
        })
        return
    end

    -- -----------------------------------------------------------------------
    -- Cas 2 : machine occupée par moi (transaction en cours)
    -- -----------------------------------------------------------------------
    if IsValid(user) and user == LocalPlayer() then
        Laundromat.UI.OpenProgressMenu(machine)
        return
    end

    -- -----------------------------------------------------------------------
    -- Cas 1 : machine libre
    -- -----------------------------------------------------------------------
    IsProgressView = false
    local moneyInput = nil
    local duration   = (Laundromat.Config and Laundromat.Config.Duration) or 30
    local ratio      = (Laundromat.Config and Laundromat.Config.Ratio)    or 0.60
    local minAmt     = (Laundromat.Config and Laundromat.Config.MinAmount) or 100

    local function buildContent(body)
        local pad    = S(12)
        local innerW = body:GetWide() - pad * 2

        -- Solde sale
        local statCard = DBALib.UI.StatCard({
            parent = body,
            value  = DBALib.Format.Money(CurrentDirty),
            label  = "Argent sale en poche",
        })
        statCard:SetWide(innerW)

        local divider = DBALib.UI.Divider({ parent = body })
        divider:SetWide(innerW)

        -- Texte explicatif
        local infoLabel = vgui.Create("DLabel", body)
        infoLabel:SetText(string.format(
            "Ratio fixe : %.0f%%   —   Durée : %ds   —   Aucune surprise.",
            ratio * 100, duration
        ))
        infoLabel:SetFont((DBALib.Fonts and DBALib.Fonts.Body_sm) or "DermaDefault")
        infoLabel:SetTextColor((DBALib.Theme and DBALib.Theme.palette and DBALib.Theme.palette.text
                                and DBALib.Theme.palette.text.muted) or Color(180, 180, 180))
        infoLabel:SizeToContents()
        infoLabel:SetWide(innerW)

        -- MoneyInput
        local defaultAmt = math.min(10000, CurrentDirty)
        defaultAmt = math.max(defaultAmt, minAmt)

        moneyInput = DBALib.UI.MoneyInput({
            parent = body,
            value  = defaultAmt,
            min    = minAmt,
            max    = math.min(CurrentDirty, (Laundromat.Config and Laundromat.Config.MaxAmount) or 1000000000),
        })
        moneyInput:SetWide(innerW)

        -- Bouton Insérer
        local insertBtn = DBALib.UI.Button({
            parent  = body,
            label   = "Insérer",
            variant = "primary",
            size    = "lg",
            onClick = function()
                if not IsValid(CurrentMachine) then
                    Laundromat.UI.OnError("Machine invalide.")
                    return
                end

                local amount = math.floor(moneyInput:GetValue() or 0)
                if amount < minAmt then
                    DBALib.UI.Notify({
                        message  = string.format("Montant minimum : %s.", DBALib.Format.Money(minAmt)),
                        type     = "warning",
                        duration = 4,
                    })
                    return
                end

                local estimOut = math.floor(amount * ratio)

                DBALib.UI.Confirm({
                    title   = "Confirmer le blanchiment",
                    message = string.format(
                        "Blanchir %s ?\n\nVous récupérerez exactement %s (%.0f%% fixe).",
                        DBALib.Format.Money(amount),
                        DBALib.Format.Money(estimOut),
                        ratio * 100
                    ),
                    onYes = function()
                        net.Start("Laundromat.RequestStart")
                            net.WriteEntity(CurrentMachine)
                            net.WriteUInt(amount, 30)
                        net.SendToServer()
                        closeFrame()
                    end,
                })
            end,
        })
        insertBtn:SetWide(innerW)

        DBALib.UI.VBox({
            parent  = body,
            items   = { statCard, divider, infoLabel, moneyInput, insertBtn },
            gap     = S(10),
            padding = pad,
        })
    end

    CurrentFrame = DBALib.UI.Frame({
        title     = "Laverie automatique",
        w         = 480,
        h         = 360,
        draggable = false,
        closable  = true,
        content   = buildContent,
    })

    if IsValid(CurrentFrame) then
        local origClose = CurrentFrame.OnClose
        CurrentFrame.OnClose = function(self)
            hook.Remove("Think", "Laundromat.ProgressTick")
            CurrentFrame   = nil
            ProgressBarRef = nil
            if origClose then origClose(self) end
        end
    end
end

-- ============================================================================
-- Laundromat.UI.OpenProgressMenu
-- Cas 2 : ouvre le menu de suivi de progression (transaction en cours pour moi)
-- ============================================================================

function Laundromat.UI.OpenProgressMenu(machine)
    if not dbaLibReady() then return end
    if IsValid(CurrentFrame) then closeFrame() return end

    if not IsValid(machine) then return end

    IsProgressView = true
    CurrentMachine = machine

    local startedAt = machine:GetNW2Float("laundromat_startedAt", 0)
    local endsAt    = machine:GetNW2Float("laundromat_endsAt", 0)
    local amount    = machine:GetNW2Int("laundromat_amount", 0)
    local duration  = math.max(endsAt - startedAt, 0.001)

    DBALib.Sound("ui_open")

    local S = DBALib.Scale

    local function buildContent(body)
        local pad    = S(12)
        local innerW = body:GetWide() - pad * 2

        local statCard = DBALib.UI.StatCard({
            parent = body,
            value  = DBALib.Format.Money(amount),
            label  = "Blanchiment en cours…",
        })
        statCard:SetWide(innerW)

        ProgressBarRef = DBALib.UI.ProgressBar({
            parent  = body,
            value   = 0,
            max     = duration,
            variant = "linear",
            label   = "Progression",
        })
        ProgressBarRef:SetWide(innerW)

        local cancelBtn = DBALib.UI.Button({
            parent  = body,
            label   = "Annuler",
            variant = "danger",
            onClick = function()
                net.Start("Laundromat.CancelRequest")
                    net.WriteEntity(CurrentMachine)
                net.SendToServer()
                closeFrame()
            end,
        })
        cancelBtn:SetWide(innerW)

        DBALib.UI.VBox({
            parent  = body,
            items   = { statCard, ProgressBarRef, cancelBtn },
            gap     = S(12),
            padding = pad,
        })
    end

    CurrentFrame = DBALib.UI.Frame({
        title     = "Laverie — Cycle en cours",
        w         = 480,
        h         = 240,
        draggable = false,
        closable  = true,
        content   = buildContent,
    })

    if IsValid(CurrentFrame) then
        local origClose = CurrentFrame.OnClose
        CurrentFrame.OnClose = function(self)
            hook.Remove("Think", "Laundromat.ProgressTick")
            CurrentFrame   = nil
            ProgressBarRef = nil
            if origClose then origClose(self) end
        end
    end

    -- Think throttlé pour la progress bar (lecture depuis NW2 vars)
    hook.Add("Think", "Laundromat.ProgressTick", function()
        if not IsValid(ProgressBarRef) then
            hook.Remove("Think", "Laundromat.ProgressTick")
            return
        end

        if (ProgressBarRef._nextTick or 0) > CurTime() then return end
        ProgressBarRef._nextTick = CurTime() + 0.033  -- ~30 Hz

        if not IsValid(CurrentMachine) then return end

        local sAt = CurrentMachine:GetNW2Float("laundromat_startedAt", 0)
        local eAt = CurrentMachine:GetNW2Float("laundromat_endsAt", 0)
        local dur = math.max(eAt - sAt, 0.001)
        local elapsed = math.Clamp(CurTime() - sAt, 0, dur)

        ProgressBarRef:SetValue(elapsed)
    end)
end

-- ============================================================================
-- Laundromat.UI.OnError
-- ============================================================================

function Laundromat.UI.OnError(msg)
    if not dbaLibReady() then return end

    DBALib.Sound("ui_error")
    DBALib.UI.Notify({
        message  = tostring(msg),
        type     = "danger",
        duration = 5,
    })
end

-- ============================================================================
-- Réponse aux hooks Laundering (toasts — pour le cas où ce menu est ouvert
-- pendant un cycle géré par laundering_system)
-- Les callbacks Laundering.Started / Completed / Cancelled déjà gérés par
-- laundering_system/cl_net.lua ; on écoute ici pour fermer la frame laundromat.
-- ============================================================================

-- On écoute le hook local (pas un net message) — quand laundering_system
-- envoie Laundering.Completed au client via net, cl_net.lua de laundering
-- appelle Laundering.UI.OnCompleted. On close juste notre frame.
-- Pour éviter la dépendance directe, on observe juste les NW2 vars : si
-- endsAt passe à 0, la machine est libérée → on ferme si ouverte.

hook.Add("Think", "Laundromat.WatchMachineFree", function()
    if not IsValid(CurrentFrame) then return end
    if not IsProgressView then return end  -- ne ferme que la vue "cycle en cours", jamais la vue "machine libre"
    if not IsValid(CurrentMachine) then
        closeFrame()
        return
    end

    -- Throttle
    if (Laundromat._watchTick or 0) > CurTime() then return end
    Laundromat._watchTick = CurTime() + 0.5

    local user = CurrentMachine:GetNW2Entity("laundromat_user")
    if not IsValid(user) then
        -- Transaction terminée ou annulée côté serveur → fermer silencieusement
        closeFrame()
    end
end)
