// *************************************************************************
// Ma_checker_baninfo.sp
// Плагин для проверки банов и мутов игроков через меню.
// Требует установленную / рабочую базу Material Admin / SourceBans++.
// *************************************************************************

#include <sourcemod>
#include <sdktools>
#include <materialadmin>

#define MAX_STEAMID_LENGTH   32
#define AC_MAX_NAME_LENGTH   64
#define MAX_IP_LENGTH        64
#define PLUGIN_VERSION "1.2"

Database g_dDatabase = null;
char g_sDatabasePrefix[12] = "sb";
bool g_bDatabaseConnected = false;


enum CheckType
{
    Check_Bans = 0,
    Check_Comms
};


CheckType g_CurrentCheckType[MAXPLAYERS+1];

public Plugin myinfo =
{
    name = "[MA] Ban info checker",
    author = "DoctorishHD",
    description = "Меню для проверки банов и мутов игроков",
    version = PLUGIN_VERSION,
    url = "https://extremeshot.ru/"
};

public void OnPluginStart()
{
    RegAdminCmd("sm_acheck", Cmd_ACheck, ADMFLAG_BAN);
}


public void MAOnConfigSetting()
{
    if (!MAGetConfigSetting("DatabasePrefix", g_sDatabasePrefix))
    {
        strcopy(g_sDatabasePrefix, sizeof(g_sDatabasePrefix), "sb");
    }
    PrintToServer("[CheckBi] Префикс базы данных: %s", g_sDatabasePrefix);
}

// ПОДКЛЮЧЕНИЕ БАЗЫ ДАННЫХ
public void MAOnConnectDatabase(Database db)
{
    g_dDatabase = db;
    g_bDatabaseConnected = true;
    PrintToServer("[CheckBi] База данных успешно подключена!");
}

// Проверяем подключение к базе данных
bool IsDatabaseReady()
{
    return g_bDatabaseConnected && g_dDatabase != null;
}

// Функция для переподключения базы (аналогично ma_bd_connect из Material Admin)
void ReconnectDatabase(int client = 0)
{
    // Сбрасываем флаг подключения
    g_bDatabaseConnected = false;
    g_dDatabase = null;
    
    // Запрашиваем переподключение через Material Admin
    if (LibraryExists("materialadmin"))
    {
        // Используем ту же команду, что и Material Admin
        ServerCommand("ma_bd_connect");
        
        if (client > 0 && IsClientInGame(client))
        {
            PrintToChat(client, "[CheckBi] Запрашиваю переподключение базы данных...");
        }
        else
        {
            PrintToServer("[CheckBi] Запрашиваю переподключение базы данных...");
        }
        
        // Ждем немного и проверяем подключение
        CreateTimer(3.0, Timer_CheckReconnect, GetClientUserId(client));
    }
    else
    {
        if (client > 0 && IsClientInGame(client))
        {
            PrintToChat(client, "[CheckBi] Material Admin не найден!");
        }
        PrintToServer("[CheckBi] Material Admin не найден!");
    }
}

public Action Timer_CheckReconnect(Handle timer, int userid)
{
    int client = GetClientOfUserId(userid);
    
    if (g_bDatabaseConnected)
    {
        if (client > 0 && IsClientInGame(client))
        {
            PrintToChat(client, "[CheckBi] База данных переподключена успешно!");
        }
        PrintToServer("[CheckBi] База данных переподключена успешно!");
    }
    else
    {
        if (client > 0 && IsClientInGame(client))
        {
            PrintToChat(client, "[CheckBi] Не удалось переподключить базу данных!");
        }
        PrintToServer("[CheckBi] Не удалось переподключить базу данных!");
    }
    
    return Plugin_Stop;
}

// Команда для переподключения базы (аналогично ma_bd_connect)
public Action Cmd_ACheck(int client, int args)
{
    if (!client || !IsClientInGame(client))
    {
        return Plugin_Handled;
    }


    if (args > 0)
    {
        char arg[32];
        GetCmdArg(1, arg, sizeof(arg));
        
        if (StrEqual(arg, "reload", false) || StrEqual(arg, "reconnect", false))
        {
            ReconnectDatabase(client);
            return Plugin_Handled;
        }
    }


    if (!IsDatabaseReady())
    {
        PrintToChat(client, "[CheckBi] База данных не готова. Попробуйте позже.");
        PrintToChat(client, "[CheckBi] Используйте: sm_acheck reload");
        return Plugin_Handled;
    }

    Menu menu = new Menu(MenuHandler_CheckType);
    menu.SetTitle("Что проверяем?");
    menu.AddItem("bans", "Баны");
    menu.AddItem("comms", "Муты/Гаги");
    menu.ExitButton = true;
    menu.Display(client, 0);

    return Plugin_Handled;
}

public int MenuHandler_CheckType(Menu menu, MenuAction action, int client, int param)
{
    if (action == MenuAction_Select)
    {
        char info[16];
        menu.GetItem(param, info, sizeof(info));

        CheckType type = (StrEqual(info, "bans")) ? Check_Bans : Check_Comms;
        g_CurrentCheckType[client] = type; 
        ShowPlayerMenu(client, type);
    }
    else if (action == MenuAction_End)
    {
        delete menu;
    }
    return 0;
}

// Второе меню: список игроков
void ShowPlayerMenu(int client, CheckType type)
{
    if (!IsDatabaseReady())
    {
        PrintToChat(client, "[CheckBi] База данных отключена");
        PrintToChat(client, "[CheckBi] Используйте: sm_acheck reload");
        return;
    }

    Menu menu = new Menu(MenuHandler_PlayerSelect);

    if (type == Check_Bans)
    {
        menu.SetTitle("Выберите игрока (Баны)");
    }
    else
    {
        menu.SetTitle("Выберите игрока (Муты)");
    }

    char userid[12], name[AC_MAX_NAME_LENGTH], display[AC_MAX_NAME_LENGTH + 10];
    bool foundPlayers = false;
    
    for (int i = 1; i <= MaxClients; i++)
    {
        if (IsClientInGame(i) && !IsFakeClient(i))
        {
            IntToString(GetClientUserId(i), userid, sizeof(userid));
            GetClientName(i, name, sizeof(name));
            Format(display, sizeof(display), "%s (%d)", name, i);
            menu.AddItem(userid, display);
            foundPlayers = true;
        }
    }

    if (!foundPlayers)
    {
        menu.AddItem("", "Нет игроков онлайн", ITEMDRAW_DISABLED);
    }

    menu.ExitBackButton = true;
    menu.ExitButton = true;
    menu.Display(client, 0);
}

public int MenuHandler_PlayerSelect(Menu menu, MenuAction action, int client, int param)
{
    switch (action)
    {
        case MenuAction_Select:
        {
            char useridStr[12];
            menu.GetItem(param, useridStr, sizeof(useridStr));
            int target = GetClientOfUserId(StringToInt(useridStr));
            
            if (!target || !IsClientInGame(target))
            {
                PrintToChat(client, "[CheckBi] Игрок больше не в игре");
                
                ShowPlayerMenu(client, g_CurrentCheckType[client]);
                return 0;
            }

            char menuTitle[64];
            menu.GetTitle(menuTitle, sizeof(menuTitle));
            bool isBan = (StrContains(menuTitle, "Баны") != -1);

            if (!IsDatabaseReady())
            {
                PrintToChat(client, "[CheckBi] База данных отключена");
                return 0;
            }

            char steamid[MAX_STEAMID_LENGTH], ip[MAX_IP_LENGTH], query[512], name[AC_MAX_NAME_LENGTH];
            
            if (!GetClientAuthId(target, AuthId_Steam2, steamid, sizeof(steamid)) || steamid[0] == '\0')
            {
                PrintToChat(client, "[CheckBi] Не удалось получить SteamID игрока");
                
                ShowPlayerMenu(client, g_CurrentCheckType[client]);
                return 0;
            }
            
            GetClientIP(target, ip, sizeof(ip));
            GetClientName(target, name, sizeof(name));

            DataPack pack = new DataPack();
            pack.WriteCell(GetClientUserId(client)); 
            pack.WriteString(name);                  
            pack.WriteCell(g_CurrentCheckType[client]); 

            // ИСПОЛЬЗУЕМ ТОТ ЖЕ СИНТАКСИС SQL ЗАПРОСОВ, ЧТО И В РАБОЧЕМ ПЛАГИНЕ
            if (isBan)
            {
                FormatEx(query, sizeof(query), 
                    "SELECT created, ends, length, reason, RemoveType FROM `%s_bans` \
                    WHERE ((`type` = 0 AND `authid` REGEXP '^STEAM_[0-9]:%s$') OR (`type` = 1 AND `ip` = '%s')) \
                    AND ((`length` > 0 AND `ends` > UNIX_TIMESTAMP()) OR `RemoveType` IS NOT NULL) \
                    ORDER BY created DESC LIMIT 10",
                    g_sDatabasePrefix, steamid[8], ip);
            }
            else
            {
                FormatEx(query, sizeof(query), 
                    "SELECT created, ends, length, reason, RemoveType FROM `%s_comms` \
                    WHERE (`authid` REGEXP '^STEAM_[0-9]:%s$') \
                    AND ((`length` > 0 AND `ends` > UNIX_TIMESTAMP()) OR `RemoveType` IS NOT NULL) \
                    ORDER BY created DESC LIMIT 10",
                    g_sDatabasePrefix, steamid[8]);
            }

            // ВЫПОЛНЯЕМ ЗАПРОС ТАК ЖЕ, КАК В РАБОЧЕМ ПЛАГИНЕ
            if (isBan)
            {
                g_dDatabase.Query(SQL_CheckBans, query, pack, DBPrio_Low);
            }
            else
            {
                g_dDatabase.Query(SQL_CheckComms, query, pack, DBPrio_Low);
            }
        }
        case MenuAction_Cancel:
        {
            if (param == MenuCancel_ExitBack)
            {
                
                Cmd_ACheck(client, 0);
            }
        }
        case MenuAction_End:
        {
            delete menu;
        }
    }
    return 0;
}

// Обработка SQL результатов (баны)
public void SQL_CheckBans(Database db, DBResultSet results, const char[] error, DataPack pack)
{
    HandleSQLResults(results, error, pack, true);
}

// Обработка SQL результатов (муты)
public void SQL_CheckComms(Database db, DBResultSet results, const char[] error, DataPack pack)
{
    HandleSQLResults(results, error, pack, false);
}

// Общая функция обработки
void HandleSQLResults(DBResultSet results, const char[] error, DataPack pack, bool isBan)
{
    if (pack == null)
    {
        LogError("[CheckBi] HandleSQLResults: pack is null");
        return;
    }
    
    pack.Reset();
    int client = GetClientOfUserId(pack.ReadCell());
    char targetName[AC_MAX_NAME_LENGTH];
    pack.ReadString(targetName, sizeof(targetName));
    CheckType checkType = view_as<CheckType>(pack.ReadCell());
    delete pack;

    if (!client || !IsClientInGame(client))
    {
        return;
    }

    if (error[0] != '\0')
    {
        PrintToChat(client, "[CheckBi] Ошибка базы данных: %s", error);
        LogError("[CheckBi] SQL ошибка: %s", error);
        // Возвращаемся к выбору игрока
        ShowPlayerMenu(client, checkType);
        return;
    }

    if (results == null)
    {
        PrintToChat(client, "[CheckBi] Ошибка получения данных");
        LogError("[CheckBi] Результаты запроса = null");
        // Возвращаемся к выбору игрока
        ShowPlayerMenu(client, checkType);
        return;
    }

    int rowCount = results.RowCount;

    if (rowCount == 0)
    {
        PrintToChat(client, "[CheckBi] У игрока %s нет %s", targetName, isBan ? "банов" : "мутов");
        // ВОЗВРАЩАЕМСЯ К ВЫБОРУ ИГРОКА ВМЕСТО ЗАКРЫТИЯ МЕНЮ
        CreateTimer(0.1, Timer_ReturnToPlayerMenu, GetClientUserId(client));
        return;
    }

    Menu menu = new Menu(MenuHandler_HistoryClose);
    char title[128];
    Format(title, sizeof(title), "%s %s (%d записей)", 
           isBan ? "Баны" : "Муты", targetName, rowCount);
    menu.SetTitle(title);

    int counter = 0;
    while (results.FetchRow() && counter < 10)
    {
        int created = results.FetchInt(0);
        int ends = results.FetchInt(1);
        int length = results.FetchInt(2);
        char reason[128];
        results.FetchString(3, reason, sizeof(reason));
        
        char removeType[2] = " ";
        if (!results.IsFieldNull(4))
        {
            results.FetchString(4, removeType, sizeof(removeType));
        }

        char sDate[32], sEnd[32], line[256];
        FormatTime(sDate, sizeof(sDate), "%d.%m.%Y", created);
        
        if (length == 0)
        {
            strcopy(sEnd, sizeof(sEnd), "Перманентный");
        }
        else if (ends > 0)
        {
            FormatTime(sEnd, sizeof(sEnd), "%d.%m.%Y", ends);
        }
        else
        {
            strcopy(sEnd, sizeof(sEnd), "Истек");
        }

        // ПРАВИЛЬНОЕ ОБРЕЗАНИЕ ДЛИННОЙ ПРИЧИНЫ БЕЗ ЗНАКА ВОПРОСА
        if (strlen(reason) > 40)
        {
            // Копируем только первые 40 символов и добавляем многоточие
            char tempReason[44];
            strcopy(tempReason, 41, reason); // Копируем 40 символов (Можнопоставить больше но всегда +1)
            StrCat(tempReason, sizeof(tempReason), "..."); // Добавляем многоточие это тот самый +1 
            strcopy(reason, sizeof(reason), tempReason);
        }

        Format(line, sizeof(line), "%s - %s | %s", sDate, sEnd, reason);
        menu.AddItem("", line, ITEMDRAW_DISABLED);

        counter++;
    }

    
    menu.AddItem("back", "Назад к выбору игрока");
    menu.ExitButton = true;
    menu.Display(client, 20);
}


public Action Timer_ReturnToPlayerMenu(Handle timer, int userid)
{
    int client = GetClientOfUserId(userid);
    if (client > 0 && IsClientInGame(client))
    {
        ShowPlayerMenu(client, g_CurrentCheckType[client]);
    }
    return Plugin_Stop;
}

public int MenuHandler_HistoryClose(Menu menu, MenuAction action, int client, int param)
{
    switch (action)
    {
        case MenuAction_Select:
        {
            char info[16];
            menu.GetItem(param, info, sizeof(info));
            
            if (StrEqual(info, "back"))
            {
                // Возврат к выбору игрока
                ShowPlayerMenu(client, g_CurrentCheckType[client]);
                return 0; // Не удаляем меню здесь
            }
            // Если выбрана запись (заблокирована), ничего не делаем
        }
        case MenuAction_Cancel:
        {
            if (param == MenuCancel_ExitBack)
            {
                // Возврат к выбору игрока
                ShowPlayerMenu(client, g_CurrentCheckType[client]);
                return 0; // Не удаляем меню здесь
            }
        }
        case MenuAction_End:
        {
            delete menu;
        }
    }
    return 0;
}

// Функция для очистки при перезагрузке плагина
public void OnPluginEnd()
{
    g_bDatabaseConnected = false;
    g_dDatabase = null;
}

// Очищаем данные при дисконнекте клиента
public void OnClientDisconnect(int client)
{
    g_CurrentCheckType[client] = Check_Bans;
}