local PLUGIN = PLUGIN

util.AddNetworkString("ix.F4_RequestInfo")
util.AddNetworkString("ix.F4_SendInfo")
util.AddNetworkString("ix.F4_DonatePurchase")
util.AddNetworkString("ix.F4_DonatePurchaseResult")
util.AddNetworkString("ixChangeName")
util.AddNetworkString("ixF4UpdateName")

local function GetFactionCounters(factionID, podIndex, specIndex)
    local factionOnline, samePod, sameSpec = 0, 0, 0

    for _, ply in ipairs(player.GetAll()) do
        if not IsValid(ply) then continue end
        if ply:Team() ~= factionID then continue end

        factionOnline = factionOnline + 1

        local char = ply:GetCharacter()
        if not char then continue end

        if char:GetPodr() == podIndex then
            samePod = samePod + 1
        end

        if char:GetSpec() == specIndex then
            sameSpec = sameSpec + 1
        end
    end

    return factionOnline, samePod, sameSpec
end

local function BuildInfoPayload(client)
    local char = client:GetCharacter()
    if not char then return end

    local factionID = char:GetFaction()
    local factionTable = ix.faction.Get(factionID)
    local podIndex = char:GetPodr()
    local specIndex = char:GetSpec()
    local rankIndex = char:GetRank() or 1

    local factionOnline, samePod, sameSpec = GetFactionCounters(factionID, podIndex, specIndex)

    local vehiclesPlugin = ix.plugin.Get("vehicles")
    local arsenalPlugin = ix.plugin.Get("arsenal")

    local techPoints = 0
    if vehiclesPlugin and vehiclesPlugin.GetFactionPoints then
        techPoints = vehiclesPlugin:GetFactionPoints(factionID) or 0
    end

    local supplyPoints = 0
    if arsenalPlugin and arsenalPlugin.GetFactionSupply then
        supplyPoints = arsenalPlugin:GetFactionSupply(factionID) or 0
    end

    local activeVehicles = 0
    if vehiclesPlugin and vehiclesPlugin.activeVehicles then
        for _, data in pairs(vehiclesPlugin.activeVehicles) do
            if not istable(data) then continue end
            if data.faction == factionID then
                activeVehicles = activeVehicles + 1
            end
        end
    end

    local subdivisionData = {
        index = podIndex,
        name = "Не назначено",
        preset = {},
        specDefault = 1,
        members = samePod,
        model = "",
        skin = 0,
        bodygroups = {}
    }

    local specData = {
        index = specIndex,
        name = "Не назначено",
        weapons = {},
        members = sameSpec,
        podr = 0
    }

    local rankData = {
        index = rankIndex,
        name = "Без звания"
    }

    if factionTable then
        if factionTable.Podr and factionTable.Podr[podIndex] then
            local unit = factionTable.Podr[podIndex]
            subdivisionData.name = unit.name or subdivisionData.name
            subdivisionData.preset = unit.preset or subdivisionData.preset
            subdivisionData.specDefault = unit.spec_def or subdivisionData.specDefault
            subdivisionData.model = unit.model or subdivisionData.model
            subdivisionData.skin = unit.skin or subdivisionData.skin
            subdivisionData.bodygroups = unit.bodygroups or subdivisionData.bodygroups
        end

        if factionTable.Spec and factionTable.Spec[specIndex] then
            local spec = factionTable.Spec[specIndex]
            specData.name = spec.name or specData.name
            specData.weapons = spec.weapons or specData.weapons
            specData.podr = spec.podr or specData.podr
        end

        if factionTable.Ranks and factionTable.Ranks[rankIndex] then
            local rank = factionTable.Ranks[rankIndex]
            rankData.name = rank[1] or rankData.name
        end
    end

    local factionColor = nil
    if factionTable and factionTable.color then
        factionColor = { r = factionTable.color.r, g = factionTable.color.g, b = factionTable.color.b }
    end

    local supplyStatus = "Стабильно"
    local minSupply = 0
    if arsenalPlugin and arsenalPlugin.config then
        minSupply = arsenalPlugin.config.minSupply or 0
    end

    if supplyPoints <= minSupply then
        supplyStatus = "Критически низко"
    elseif supplyPoints <= minSupply * 2 and minSupply > 0 then
        supplyStatus = "Низко"
    end

    return {
        timestamp = os.time(),
        faction = {
            id = factionID,
            name = factionTable and factionTable.name or "Неизвестно",
            description = factionTable and factionTable.description or "",
            color = factionColor,
            online = factionOnline,
            techPoints = techPoints,
            supplyPoints = supplyPoints,
            supplyStatus = supplyStatus,
            activeVehicles = activeVehicles
        },
        character = {
            name = char:GetName(),
            money = char:GetMoney(),
            id = char:GetID() or 0,
            rank = rankData,
            subdivision = subdivisionData,
            spec = specData
        }
    }
end

net.Receive("ix.F4_RequestInfo", function(_, client)
    if not IsValid(client) then return end
    local payload = BuildInfoPayload(client)
    if not payload then return end

    net.Start("ix.F4_SendInfo")
        net.WriteTable(payload)
    net.Send(client)
end)

local DONATE_PURCHASE_COOLDOWN = 2
PLUGIN.donatePurchaseCooldowns = PLUGIN.donatePurchaseCooldowns or {}

local function GetCooldownKey(client)
    return client:SteamID64() or client:SteamID() or client:EntIndex()
end

function PLUGIN:GetIGSBalance(client)
    if not IsValid(client) then return 0 end

    if client.IGSFunds then
        local ok, funds = pcall(client.IGSFunds, client)
        if ok and isnumber(funds) then
            return math.max(math.floor(funds), 0)
        end
    end

    if client.GetNetVar then
        local funds = client:GetNetVar("igsFunds", 0)
        if isnumber(funds) then
            return math.max(math.floor(funds), 0)
        end
    end

    return 0
end

function PLUGIN:AdjustIGSBalance(client, amount)
    if not IsValid(client) then return false, "Игрок недоступен" end
    amount = tonumber(amount) or 0
    if amount == 0 then return true end

    if not client.AddIGSFunds then
        return false, "IGS недоступен"
    end

    local ok, err = pcall(client.AddIGSFunds, client, amount)
    if ok then
        return true
    end

    return false, err or "Ошибка IGS"
end

function PLUGIN:IsDonateOnCooldown(client)
    local key = GetCooldownKey(client)
    local expires = self.donatePurchaseCooldowns[key] or 0
    if CurTime() < expires then
        return true
    end
end

function PLUGIN:ArmDonateCooldown(client)
    local key = GetCooldownKey(client)
    self.donatePurchaseCooldowns[key] = CurTime() + DONATE_PURCHASE_COOLDOWN
end

function PLUGIN:CanPurchaseDonateProduct(client, entry)
    local char = client:GetCharacter()
    if not char then
        return false, "Нет активного персонажа"
    end

    if not entry then
        return false, "Предложение не найдено"
    end

    if entry.oneTime then
        local history = char:GetData("donate_purchases", {})
        if history[entry.id] then
            return false, "Этот набор уже был приобретен"
        end
    end

    local hookResult, hookMessage = hook.Run("CanPlayerBuyDonateProduct", client, entry)
    if hookResult == false then
        return false, hookMessage or "Покупка отклонена"
    end

    return true
end

function PLUGIN:GrantDonateItems(client, grantData)
    if not grantData then return end
    local char = client:GetCharacter()
    if not char then return end

    if istable(grantData.weapons) and #grantData.weapons > 0 then
        local owned = char:GetData("donate_weapons", {})
        local changed
        for _, class in ipairs(grantData.weapons) do
            if isstring(class) and not table.HasValue(owned, class) then
                table.insert(owned, class)
                changed = true
            end
        end
        if changed then
            char:SetData("donate_weapons", owned)
        end
    end

    if istable(grantData.cosmetics) and #grantData.cosmetics > 0 then
        local cosmetics = char:GetData("donate_cosmetics", {})
        for _, cosmeticID in ipairs(grantData.cosmetics) do
            if isstring(cosmeticID) then
                cosmetics[cosmeticID] = true
            end
        end
        char:SetData("donate_cosmetics", cosmetics)
    end

    if istable(grantData.vehicles) and #grantData.vehicles > 0 then
        local vehicles = char:GetData("donate_vehicles", {})
        for _, certificate in ipairs(grantData.vehicles) do
            if isstring(certificate) then
                vehicles[#vehicles + 1] = certificate
            end
        end
        char:SetData("donate_vehicles", vehicles)
    end

    if istable(grantData.boosts) and #grantData.boosts > 0 then
        local calls = char:GetData("donate_support_calls", {})
        for _, callID in ipairs(grantData.boosts) do
            calls[#calls + 1] = callID
        end
        char:SetData("donate_support_calls", calls)
    end

    if istable(grantData.items) and #grantData.items > 0 then
        local pending = char:GetData("donate_items", {})
        for _, itemID in ipairs(grantData.items) do
            pending[#pending + 1] = { id = itemID, ts = os.time() }
        end
        char:SetData("donate_items", pending)
    end
end

function PLUGIN:ApplyDonateReward(client, entry, mode)
    local char = client:GetCharacter()
    if not char then
        return false, "Нет активного персонажа"
    end

    local reward = entry.reward
    if not reward then
        local hookResult, hookMessage = hook.Run("OnDonateReward", client, entry, mode)
        if hookResult ~= nil then
            return hookResult, hookMessage
        end
        return false, "Награда не настроена"
    end

    local rewardType = reward.type
    
    if rewardType == "privilege" then
        local tier = reward.tier or entry.id
        local duration = nil
        
        if mode == "3month" then
            duration = os.time() + (90 * 86400) -- 90 дней
        else
            duration = os.time() + (30 * 86400) -- 30 дней (1 месяц)
        end
        
        local privileges = char:GetData("donate_privileges", {})
        privileges[tier] = {
            tier = tier,
            expires = duration,
            granted = os.time(),
            mode = mode or "month"
        }
        char:SetData("donate_privileges", privileges)
        
        -- Применяем группу через SAM если доступно
        if sam then
            local groupMap = {
                vip = "vip",
                vip_plus = "vip_plus",
                premium = "premium",
                sponsor = "sponsor"
            }
            local rank = groupMap[tier]
            if rank then
                local expireTime = mode == "3month" and os.time() + (90 * 86400) or os.time() + (30 * 86400)
                sam.player.set_rank(client, rank, expireTime)
            end
        end
        
        local modeText = mode == "3month" and "на 3 месяца" or "на 1 месяц"
        return true, reward.successMessage or ("Привилегия " .. tier .. " активирована " .. modeText)
    
    elseif rewardType == "weapon" then
        local weaponClass = reward.weaponClass or reward.class
        if not weaponClass then
            return false, "Класс оружия не указан"
        end
        
        -- Добавляем оружие в список донат-оружия персонажа со сроком действия
        local donateWeapons = char:GetData("donate_weapons_timed", {})
        if not istable(donateWeapons) then donateWeapons = {} end
        
        -- Вычисляем срок действия
        local expireTime
        if mode == "3month" then
            expireTime = os.time() + (90 * 86400) -- 90 дней
        else
            expireTime = os.time() + (30 * 86400) -- 30 дней
        end
        
        -- Добавляем или обновляем срок
        donateWeapons[weaponClass] = {
            expires = expireTime,
            granted = os.time(),
            mode = mode or "1month"
        }
        char:SetData("donate_weapons_timed", donateWeapons)
        
        local modeText = mode == "3month" and "на 3 месяца" or "на 1 месяц"
        return true, reward.successMessage or ("Донат-оружие " .. (reward.name or weaponClass) .. " добавлено в ваш арсенал " .. modeText)
    
    elseif rewardType == "money" then
        local amount = reward.amount or 0
        if amount > 0 and char.GiveMoney then
            char:GiveMoney(math.floor(amount))
            return true, reward.successMessage or ("Вам начислено " .. amount .. " ₽")
        end
        return false, "Ошибка начисления денег"
    
    elseif rewardType == "voice_chat" then
        -- Разблокировка голосового чата
        char:SetData("voice_chat_unlocked", true)
        
        -- Если есть система SAM, можно добавить флаг
        if sam then
            -- Можно добавить специальную группу или флаг для голосового чата
        end
        
        return true, reward.successMessage or "Голосовой чат разблокирован!"
        
    elseif rewardType == "bundle" then
        if reward.money and reward.money > 0 and char.GiveMoney then
            char:GiveMoney(math.floor(reward.money))
        end

        if reward.supply and reward.supply ~= 0 then
            local arsenal = ix.plugin.Get("arsenal")
            if arsenal and arsenal.AddFactionSupply then
                arsenal:AddFactionSupply(client:Team(), reward.supply)
            end
        end

        if reward.techPoints and reward.techPoints ~= 0 then
            local vehicles = ix.plugin.Get("vehicles")
            if vehicles and vehicles.AddFactionPoints then
                vehicles:AddFactionPoints(client:Team(), reward.techPoints)
            end
        end

        if reward.grantedItems then
            self:GrantDonateItems(client, reward.grantedItems)
        end

        return true, reward.successMessage or "Комплект успешно выдан"
        
    elseif rewardType == "pass" then
        local tier = reward.tier or entry.id
        local duration = math.max(1, tonumber(reward.durationDays) or 30) * 86400
        local passes = char:GetData("donate_passes", {})
        passes[tier] = {
            tier = tier,
            expires = os.time() + duration,
            bonuses = reward.bonuses or {}
        }
        char:SetData("donate_passes", passes)
        return true, reward.successMessage or "Подписка активирована"
    elseif rewardType == "booster" then
        local boosterID = reward.boosterID or entry.id
        local duration = math.max(1, tonumber(reward.durationHours) or 24) * 3600
        local boosters = char:GetData("donate_boosters", {})
        boosters[boosterID] = {
            expires = os.time() + duration,
            multiplier = reward.multiplier or 1.0
        }
        char:SetData("donate_boosters", boosters)
        return true, reward.successMessage or "Бустер активирован"
    elseif rewardType == "case" then
        local caseID = reward.caseID or entry.id
        local cases = char:GetData("donate_cases", {})
        cases[caseID] = (cases[caseID] or 0) + 1
        char:SetData("donate_cases", cases)
        return true, reward.successMessage or "Кейс добавлен в коллекцию"
    elseif rewardType == "cosmetic" then
        local unlockID = reward.unlockID or entry.id
        local cosmetics = char:GetData("donate_cosmetics", {})
        cosmetics[unlockID] = true
        char:SetData("donate_cosmetics", cosmetics)
        return true, reward.successMessage or "Косметика разблокирована"
    end

    local hookResult, hookMessage = hook.Run("OnDonateReward", client, entry)
    if hookResult ~= nil then
        return hookResult, hookMessage
    end

    return false, "Тип награды не поддерживается"
end

function PLUGIN:MarkDonatePurchase(client, entry)
    local char = client:GetCharacter()
    if not char then return end

    local history = char:GetData("donate_purchases", {})
    history[entry.id] = (history[entry.id] or 0) + 1
    char:SetData("donate_purchases", history)
end

function PLUGIN:SendDonateResult(client, success, message, productID)
    if not IsValid(client) then return end

    net.Start("ix.F4_DonatePurchaseResult")
        net.WriteBool(success and true or false)
        net.WriteString(message or "")
        net.WriteString(productID or "")
    net.Send(client)
end

function PLUGIN:HandleDonatePurchase(client, productID, price, mode)
    local entry = self:GetDonateProduct(productID)
    local ok, msg = self:CanPurchaseDonateProduct(client, entry)
    if not ok then
        return false, msg, entry
    end

    local actualPrice = price or tonumber(entry and entry.price) or 0
    if actualPrice <= 0 then
        return false, "Цена предложения не настроена", entry
    end

    local funds = self:GetIGSBalance(client)
    if funds < actualPrice then
        return false, "Недостаточно средств на балансе", entry
    end

    local debited, debitError = self:AdjustIGSBalance(client, -actualPrice)
    if not debited then
        return false, debitError or "Не удалось списать средства", entry
    end

    local success, rewardMessage = self:ApplyDonateReward(client, entry, mode)
    if not success then
        self:AdjustIGSBalance(client, actualPrice)
        return false, rewardMessage or "Ошибка выдачи награды", entry
    end

    self:MarkDonatePurchase(client, entry, mode)
    hook.Run("PostDonatePurchase", client, entry, actualPrice, mode)

    return true, rewardMessage or "Покупка завершена", entry
end

net.Receive("ix.F4_DonatePurchase", function(_, client)
    if not IsValid(client) or not client:IsPlayer() then return end

    local productID = net.ReadString() or ""
    local price = net.ReadUInt(32) or 0
    local mode = net.ReadString() or "month"

    if productID == "" then return end

    if PLUGIN:IsDonateOnCooldown(client) then
        PLUGIN:SendDonateResult(client, false, "Слишком частые запросы", productID)
        return
    end

    PLUGIN:ArmDonateCooldown(client)
    local success, message, entry = PLUGIN:HandleDonatePurchase(client, productID, price, mode)

    local entryID = entry and entry.id or productID
    if success then
        local priceText = tostring(price)
        local modeText = mode == "forever" and " (навсегда)" or " (на месяц)"
        print(string.format("[F4 Donate] %s (%s) купил %s%s за %s", client:Name(), client:SteamID(), entryID, modeText, priceText))
    else
        print(string.format("[F4 Donate] Ошибка покупки %s игроком %s: %s", entryID, client:Name(), tostring(message)))
    end

    PLUGIN:SendDonateResult(client, success, message, entryID)
end)
-- Admin command to add IGS funds
ix.command.Add("GiveIGS", {
    description = "Выдать IGS средства игроку",
    CanRun = function(self, client)
        local userGroup = string.lower(client:GetUserGroup() or "user")
        return client:IsAdmin() or AdminPrivs[userGroup]
    end,
    arguments = {
        ix.type.player,
        ix.type.number
    },
    OnRun = function(self, client, target, amount)
        if not IsValid(target) then
            return "@invalidTarget"
        end
        
        amount = math.floor(tonumber(amount) or 0)
        if amount == 0 then
            return "Укажите корректную сумму"
        end
        
        local success, err = PLUGIN:AdjustIGSBalance(target, amount)
        if success then
            local action = amount > 0 and "выдал" or "снял"
            client:Notify(string.format("Вы %s %d IGS для %s", action, math.abs(amount), target:Name()))
            target:Notify(string.format("Администратор %s вам %d IGS", action == "выдал" and "выдал" or "снял у вас", math.abs(amount)))
            
            print(string.format("[IGS Admin] %s (%s) %s %d IGS для %s (%s)", 
                client:Name(), client:SteamID(), action, math.abs(amount), target:Name(), target:SteamID()))
            return ""
        else
            return err or "Ошибка при изменении баланса"
        end
    end
})

-- Admin command to check IGS balance
ix.command.Add("CheckIGS", {
    description = "Проверить IGS баланс игрока",
    CanRun = function(self, client)
        local userGroup = string.lower(client:GetUserGroup() or "user")
        return client:IsAdmin() or AdminPrivs[userGroup]
    end,
    arguments = {
        ix.type.player
    },
    OnRun = function(self, client, target)
        if not IsValid(target) then
            return "@invalidTarget"
        end
        
        local balance = PLUGIN:GetIGSBalance(target)
        return string.format("Баланс IGS игрока %s: %d руб.", target:Name(), balance)
    end
})

net.Receive("ixChangeName", function(len, client)
    if not IsValid(client) then return end
    
    local character = client:GetCharacter()
    if not character then return end
    
    local newName = net.ReadString()
    if not newName or newName == "" then return end
    
    if #newName < 3 or #newName > 50 then
        client:Notify("Имя должно содержать от 3 до 50 символов")
        return
    end
    
    if newName:find("[<>\"\\/]") then
        client:Notify("Имя содержит недопустимые символы")
        return
    end
    
    local oldName = character:GetName()
    character:SetName(newName)
    client:Notify("Ваше имя успешно изменено на: " .. newName)

    net.Start("ixF4UpdateName")
    net.WriteString(newName)
    net.Send(client)

    hook.Run("OnCharacterVarChanged", character, "name", oldName, newName)
end)

-- Фикс выдачи донат-оружия IGS в Helix
hook.Add("PostPlayerLoadout", "IGS_Helix_WeaponLoadoutFix", function(client)
    if not IsValid(client) or not IGS then return end

    timer.Simple(1.5, function()
        if not IsValid(client) or not client:GetCharacter() then return end
        
        if isfunction(IGS.PlayerLoadout) then
            IGS.PlayerLoadout(client)
        end

        if isfunction(IGS.GetItems) and IsValid(client) and client.HasPurchase then
            for _, ITEM in pairs(IGS.GetItems()) do
                if not ITEM or not isfunction(ITEM.GetUID) then continue end
                
                local weaponClass = (isfunction(ITEM.GetWeapon) and ITEM:GetWeapon()) or ITEM.weapon
                if weaponClass and client:HasPurchase(ITEM:GetUID()) then
                    if not client:HasWeapon(weaponClass) then
                        client:Give(weaponClass)
                    end
                end
            end
        end
    end)
end)
