local PLUGIN = PLUGIN
util.AddNetworkString("ixPromoCodeActivate")
util.AddNetworkString("ixPromoCodeResult")
util.AddNetworkString("ixPromoCodeCreate")
util.AddNetworkString("ixPromoCodeDelete")
util.AddNetworkString("ixPromoCodeList")
util.AddNetworkString("ixPromoCodeSync")
PLUGIN.promoCodes = PLUGIN.promoCodes or {}
PLUGIN.promoUsage = PLUGIN.promoUsage or {} -- [code] = {[steamID] = true}

-- Путь к файлу сохранения
local dataPath = "militaryrp/promocodes.txt"

-- Сохранение промокодов
function PLUGIN:SavePromoCodes()
    local data = {
        codes = self.promoCodes,
        usage = self.promoUsage
    }
    
    file.CreateDir("militaryrp")
    file.Write(dataPath, util.TableToJSON(data))
    print("[PROMOCODES] Промокоды сохранены")
end

-- Загрузка промокодов
function PLUGIN:LoadPromoCodes()
    if file.Exists(dataPath, "DATA") then
        local jsonData = file.Read(dataPath, "DATA")
        local data = util.JSONToTable(jsonData)
        
        if data then
            self.promoCodes = data.codes or {}
            self.promoUsage = data.usage or {}
            print("[PROMOCODES] Загружено " .. table.Count(self.promoCodes) .. " промокодов")
        end
    else
        print("[PROMOCODES] Файл промокодов не найден, создается новый")
    end
end

-- Генерация случайного кода
function PLUGIN:GenerateCode(length)
    local chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    local code = ""
    
    for i = 1, length do
        local rand = math.random(1, #chars)
        code = code .. string.sub(chars, rand, rand)
    end
    
    return code
end

-- Валидация именного промокода
function PLUGIN:ValidateCustomCode(code)
    -- Проверка на пустоту
    if not code or code == "" then
        return false, "Код не может быть пустым"
    end
    
    -- Проверка длины (минимум 3, максимум 32 символа)
    if #code < 3 then
        return false, "Код должен содержать минимум 3 символа"
    end
    
    if #code > 32 then
        return false, "Код должен содержать максимум 32 символа"
    end
    
    -- Проверка на допустимые символы (только A-Z, 0-9 и _)
    if not string.match(code, "^[A-Z0-9_]+$") then
        return false, "Код может содержать только буквы A-Z, цифры 0-9 и символ подчеркивания _"
    end
    
    return true, "OK"
end

-- Создание промокода
function PLUGIN:CreatePromoCode(admin, codeText, amount, maxUses, expiresAt, allowedRanks)
    if not IsValid(admin) then return false, "Недействительный игрок" end
    
    -- Проверка прав
    if not admin:IsSuperAdmin() then
        return false, "У вас нет прав для создания промокодов"
    end
    
    -- Генерация кода если не указан
    if not codeText or codeText == "" then
        codeText = self:GenerateCode(8)
        print(string.format("[PROMOCODES] Сгенерирован случайный промокод: %s", codeText))
    else
        -- Валидация именного кода
        local valid, error = self:ValidateCustomCode(codeText)
        if not valid then
            return false, error
        end
        print(string.format("[PROMOCODES] Создается именной промокод: %s", codeText))
    end
    
    codeText = string.upper(codeText)
    
    -- Проверка существования
    if self.promoCodes[codeText] then
        return false, "Промокод с таким кодом уже существует"
    end
    
    -- Создание промокода
    self.promoCodes[codeText] = {
        code = codeText,
        amount = math.max(1, tonumber(amount) or 100),
        maxUses = math.max(1, tonumber(maxUses) or 1),
        currentUses = 0,
        expiresAt = tonumber(expiresAt) or (os.time() + 86400 * 30), -- По умолчанию 30 дней
        allowedRanks = allowedRanks or {}, -- Пустой массив = доступно всем
        createdBy = admin:SteamID(),
        createdAt = os.time()
    }
    
    self.promoUsage[codeText] = {}
    self:SavePromoCodes()
    
    return true, "Промокод '" .. codeText .. "' создан на " .. self.promoCodes[codeText].amount .. " IGS"
end

-- Удаление промокода
function PLUGIN:DeletePromoCode(admin, codeText)
    if not IsValid(admin) then return false, "Недействительный игрок" end
    
    if not admin:IsSuperAdmin() then
        return false, "У вас нет прав для удаления промокодов"
    end
    
    codeText = string.upper(codeText)
    
    if not self.promoCodes[codeText] then
        return false, "Промокод не найден"
    end
    
    self.promoCodes[codeText] = nil
    self.promoUsage[codeText] = nil
    self:SavePromoCodes()
    
    return true, "Промокод '" .. codeText .. "' удален"
end

-- Проверка доступности промокода для игрока
function PLUGIN:CanUsePromoCode(client, codeText)
    local promo = self.promoCodes[codeText]
    if not promo then
        return false, "Промокод не найден"
    end
    
    local steamID = client:SteamID()
    
    -- Проверка использования игроком
    if self.promoUsage[codeText] and self.promoUsage[codeText][steamID] then
        return false, "Вы уже использовали этот промокод"
    end
    
    -- Проверка лимита использований
    if promo.currentUses >= promo.maxUses then
        return false, "Промокод исчерпан"
    end
    
    -- Проверка срока действия
    if os.time() > promo.expiresAt then
        return false, "Срок действия промокода истек"
    end
    
    -- Проверка доступных рангов
    if promo.allowedRanks and #promo.allowedRanks > 0 then
        local playerRank = client:GetUserGroup()
        local hasAccess = false
        
        for _, rank in ipairs(promo.allowedRanks) do
            if playerRank == rank then
                hasAccess = true
                break
            end
        end
        
        if not hasAccess then
            return false, "Этот промокод недоступен для вашего ранга"
        end
    end
    
    return true, "OK"
end

-- Активация промокода
function PLUGIN:ActivatePromoCode(client, codeText)
    if not IsValid(client) then return false, "Недействительный игрок" end
    
    codeText = string.upper(string.Trim(codeText))
    
    if codeText == "" then
        return false, "Введите промокод"
    end
    
    local canUse, reason = self:CanUsePromoCode(client, codeText)
    if not canUse then
        return false, reason
    end
    
    local promo = self.promoCodes[codeText]
    local steamID = client:SteamID()
    
    -- Начисление валюты через F4 плагин
    local f4Plugin = ix.plugin.Get("f4menu")
    if f4Plugin and f4Plugin.AdjustIGSBalance then
        local success, error = f4Plugin:AdjustIGSBalance(client, promo.amount)
        if not success then
            return false, "Ошибка начисления валюты: " .. (error or "неизвестная ошибка")
        end
    else
        return false, "Система доната недоступна"
    end
    
    -- Обновление данных промокода
    promo.currentUses = promo.currentUses + 1
    
    if not self.promoUsage[codeText] then
        self.promoUsage[codeText] = {}
    end
    self.promoUsage[codeText][steamID] = true
    
    self:SavePromoCodes()
    
    -- Лог
    print(string.format("[PROMOCODES] %s активировал промокод '%s' (+%d IGS)", client:Name(), codeText, promo.amount))
    
    return true, string.format("Промокод активирован! Вы получили %d IGS", promo.amount)
end

-- Загрузка при старте
function PLUGIN:InitializedPlugins()
    self:LoadPromoCodes()
end

-- Сетевые обработчики
net.Receive("ixPromoCodeActivate", function(len, client)
    local code = net.ReadString()
    
    local plugin = ix.plugin.Get("promocodes")
    if not plugin then return end
    
    local success, message = plugin:ActivatePromoCode(client, code)
    
    net.Start("ixPromoCodeResult")
        net.WriteBool(success)
        net.WriteString(message)
    net.Send(client)
end)

net.Receive("ixPromoCodeCreate", function(len, client)
    if not client:IsSuperAdmin() then return end
    
    local code = net.ReadString()
    local amount = net.ReadUInt(32)
    local maxUses = net.ReadUInt(16)
    local expiresAt = net.ReadUInt(32)
    local ranksCount = net.ReadUInt(8)
    
    local allowedRanks = {}
    for i = 1, ranksCount do
        table.insert(allowedRanks, net.ReadString())
    end
    
    local plugin = ix.plugin.Get("promocodes")
    if not plugin then return end
    
    local success, message = plugin:CreatePromoCode(client, code, amount, maxUses, expiresAt, allowedRanks)
    
    net.Start("ixPromoCodeResult")
        net.WriteBool(success)
        net.WriteString(message)
    net.Send(client)
    
    if success then
        -- Отправляем обновленный список
        plugin:SyncPromoCodesList(client)
    end
end)

net.Receive("ixPromoCodeDelete", function(len, client)
    if not client:IsSuperAdmin() then return end
    
    local code = net.ReadString()
    
    local plugin = ix.plugin.Get("promocodes")
    if not plugin then return end
    
    local success, message = plugin:DeletePromoCode(client, code)
    
    net.Start("ixPromoCodeResult")
        net.WriteBool(success)
        net.WriteString(message)
    net.Send(client)
    
    if success then
        plugin:SyncPromoCodesList(client)
    end
end)

net.Receive("ixPromoCodeList", function(len, client)
    if not client:IsSuperAdmin() then return end
    
    local plugin = ix.plugin.Get("promocodes")
    if not plugin then return end
    
    plugin:SyncPromoCodesList(client)
end)

-- Синхронизация списка промокодов
function PLUGIN:SyncPromoCodesList(client)
    if not IsValid(client) or not client:IsSuperAdmin() then return end
    
    local codesList = {}
    for code, data in pairs(self.promoCodes) do
        table.insert(codesList, data)
    end
    
    net.Start("ixPromoCodeSync")
        net.WriteString(util.TableToJSON(codesList))
    net.Send(client)
end
