Документацію для цього модуля можна створити у Модуль:FetchData/документація

local p = {}

-- ================================================
-- ГЛОБАЛЬНЕ КЕШУВАННЯ
-- ================================================

local page_cache = {}
local table_cache = {}

-- ================================================
-- УТИЛІТИ
-- ================================================

local function clean_wikilinks(text)
    if not text then return nil end
    text = mw.ustring.gsub(text, "%[%[([^%]|]+)|([^%]]+)%]%]", "%2")
    text = mw.ustring.gsub(text, "%[%[([^%]]+)%]%]", "%1")
    return mw.text.trim(text)
end

local function error_output(message)
    return '<span style="color:red; font-weight:bold;">Помилка: ' .. message .. '</span>'
end

local function get_page_content(page_name)
    if page_cache[page_name] then
        return page_cache[page_name]
    end
    
    local title = mw.title.new(page_name)
    if not title or not title.exists then
        return nil
    end
    
    local content = title:getContent()
    page_cache[page_name] = content
    return content
end

local function get_table_from_page(page_name)
    if table_cache[page_name] then
        return table_cache[page_name]
    end
    
    local content = get_page_content(page_name)
    if not content then return nil end
    
    local table_start = mw.ustring.find(content, "{|")
    local table_end = mw.ustring.find(content, "|}", table_start)
    
    if not table_start or not table_end then return nil end
    
    local table_content = mw.ustring.sub(content, table_start, table_end + 1)
    table_cache[page_name] = table_content
    return table_content
end

local function parse_table_rows(table_content)
    local rows = {}
    for row in mw.ustring.gmatch(table_content, "|-\n(.-)\n|-") do
        table.insert(rows, row)
    end
    local last_row = mw.ustring.match(table_content, "|-\n(.-)%s*|}")
    if last_row then
        table.insert(rows, last_row)
    end
    return rows
end

local function parse_row_cells(row)
    local cells = {}
    row = mw.ustring.gsub(row, "^%s*|%s*", "")
    
    for cell in mw.ustring.gmatch(row, "|([^|]+)") do
        local trimmed = mw.text.trim(cell)
        if trimmed ~= "" then
            table.insert(cells, trimmed)
        end
    end
    
    return cells
end

local function find_player_data(table_content, player_name)
    if not table_content then return nil end
    
    local escaped = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
    local player_pattern = "%[%[" .. escaped .. "%]%]"
    
    local rows = parse_table_rows(table_content)
    
    for _, row in ipairs(rows) do
        if mw.ustring.find(row, player_pattern) then
            return parse_row_cells(row)
        end
    end
    
    return nil
end

-- Конвертація часу (якщо потрібно)
local function format_time(time_str)
    if not time_str or time_str == "" then return "" end
    
    time_str = mw.text.trim(time_str)
    
    -- Якщо вже в правильному форматі
    if mw.ustring.find(time_str, "хв") then
        return time_str
    end
    
    -- Парсимо формат HH:MM:SS або MM:SS
    local hours, minutes, seconds = mw.ustring.match(time_str, "(%d+):(%d+):(%d+)")
    
    if not hours then
        minutes, seconds = mw.ustring.match(time_str, "(%d+):(%d+)")
        hours = "0"
    end
    
    if minutes and seconds then
        hours = tonumber(hours) or 0
        minutes = tonumber(minutes) or 0
        seconds = tonumber(seconds) or 0
        
        if hours > 0 then
            return string.format("%d год %02d хв %02d с", hours, minutes, seconds)
        else
            return string.format("%d хв %02d с", minutes, seconds)
        end
    end
    
    return time_str
end

-- ================================================
-- ФУНКЦІЇ З FETCHDATA
-- ================================================

function p.season_result(frame)
    local player_name = frame.args.player or frame.args[1]
    local season_number = frame.args.season or frame.args[2]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    if not season_number or season_number == "" then
        return error_output("Не вказано номер сезону")
    end
    
    local season_page = nil
    local season_names = {
        ["1"] = "Перший сезон",
        ["2"] = "Другий сезон",
        ["3"] = "Третій сезон",
        ["4"] = "Четвертий сезон",
        ["5"] = "П'ятий сезон",
        ["6"] = "Шостий сезон",
        ["7"] = "Сьомий сезон",
        ["8"] = "Восьмий сезон",
        ["9"] = "Дев'ятий сезон"
    }
    
    season_page = season_names[season_number]
    
    if not season_page then
        return error_output("Невірний номер сезону")
    end
    
    local content = get_page_content(season_page)
    if not content then
        return error_output("Не вдалося завантажити сторінку сезону")
    end
    
    local final_section = mw.ustring.match(content, "==+%s*Фінал%s*==+(.-)\n==")
    if not final_section then
        final_section = mw.ustring.match(content, "==+%s*Фінал%s*==+(.*)")
    end
    
    if not final_section then
        return error_output("Не знайдено секцію Фінал")
    end
    
    local table_start = mw.ustring.find(final_section, "{|")
    local table_end = mw.ustring.find(final_section, "|}", table_start)
    
    if not table_start or not table_end then
        return error_output("Не знайдено таблицю у секції Фінал")
    end
    
    local table_content = mw.ustring.sub(final_section, table_start, table_end + 1)
    local player_data = find_player_data(table_content, player_name)
    
    if not player_data or #player_data < 2 then
        return "—"
    end
    
    local place = player_data[1]
    return place or "—"
end

-- ================================================
-- ФУНКЦІЇ З FETCHDATA2
-- ================================================

function p.games_count(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Статистика")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 3 then
        return "0"
    end
    
    return data[3] or "0"
end

function p.wins_count(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Статистика")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 4 then
        return "0"
    end
    
    return data[4] or "0"
end

function p.losses_count(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Статистика")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 5 then
        return "0"
    end
    
    return data[5] or "0"
end

function p.win_rate_colored(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Статистика")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 6 then
        return '<span style="color:indianred;">0%</span>'
    end
    
    local winrate = data[6]
    winrate = mw.ustring.gsub(winrate, "%%", "")
    winrate = mw.text.trim(winrate)
    
    local rate = tonumber(winrate) or 0
    local color = rate < 40 and "indianred" or "#4caf50"
    
    return '<span style="color:' .. color .. ';">' .. winrate .. '%</span>'
end

function p.recruiter(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Гравці")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 4 then
        return "Не вказано"
    end
    
    local recruiter = mw.text.trim(data[4])
    
    if recruiter == "Відсутній" or recruiter == "-" or recruiter == "" then
        return "Не вказано"
    end
    
    return recruiter
end

function p.date_added(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Гравці")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 3 then
        return "Лише Бог знає"
    end
    
    local raw_date = data[3]
    
    if not raw_date or raw_date == "" or raw_date == "-" then
        return "Лише Бог знає"
    end
    
    local day, month, year = mw.ustring.match(raw_date, "(%d+)%.(%d+)%.(%d+)")
    
    if not day or not month or not year then
        return raw_date
    end
    
    local end_date = os.time({year=2024, month=10, day=26})
    local start_date = os.time({year=tonumber(year), month=tonumber(month), day=tonumber(day)})
    local days_diff = math.floor((end_date - start_date) / 86400)
    
    return string.format("%s (%d днів)", raw_date, days_diff)
end

function p.foundation(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Фундація")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 3 then
        return "0"
    end
    
    local amount = mw.ustring.gsub(data[3], "[^%d]", "")
    return amount
end

function p.prize_pool(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Призовий_фонд")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 2 then
        return "0"
    end
    
    local amount = mw.ustring.gsub(data[2], "[^%d]", "")
    return amount
end

function p.finalist(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local table_content = get_table_from_page("Фіналіст")
    if not table_content then
        return error_output("Не вдалося завантажити таблицю")
    end
    
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 3 then
        return "0"
    end
    
    local count = mw.ustring.gsub(data[3], "[^%d]", "")
    return count
end

function p.foty_rating(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local content = get_page_content("Період")
    if not content then
        return "—"
    end
    
    local foty_section = mw.ustring.match(content, "==+%s*Фінал Року%s*==+(.-)\n==")
    if not foty_section then
        foty_section = mw.ustring.match(content, "==+%s*Фінал Року%s*==+(.*)")
    end
    
    if not foty_section then
        return "—"
    end
    
    local table_start = mw.ustring.find(foty_section, "{|")
    local table_end = mw.ustring.find(foty_section, "|}", table_start)
    
    if not table_start or not table_end then
        return "—"
    end
    
    local table_content = mw.ustring.sub(foty_section, table_start, table_end + 1)
    local data = find_player_data(table_content, player_name)
    
    if not data or #data < 3 then
        return "—"
    end
    
    local place = mw.text.trim(data[1])
    local rating = mw.text.trim(data[3])
    
    rating = mw.ustring.gsub(rating, "<span[^>]*>", "")
    rating = mw.ustring.gsub(rating, "</span>", "")
    rating = mw.ustring.gsub(rating, "[^%d%-]", "")
    rating = mw.text.trim(rating)
    
    if rating and rating ~= "" then
        return rating .. " (" .. place .. " місце)"
    end
    
    return "—"
end

-- ================================================
-- ФУНКЦІЇ З FETCHDATA5 (player_games)
-- ================================================

function p.player_games(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local page_content = get_page_content("Записи_ігор")
    if not page_content then
        return error_output("Не вдалося завантажити сторінку Записи_ігор")
    end
    
    local table_start = mw.ustring.find(page_content, "{|")
    local table_end = mw.ustring.find(page_content, "|}", table_start)
    
    if not table_start or not table_end then
        return error_output("Не знайдено таблицю")
    end
    
    local table_content = mw.ustring.sub(page_content, table_start, table_end + 1)
    local rows = parse_table_rows(table_content)
    
    if #rows < 1 then
        return error_output("Таблиця порожня")
    end
    
    local escaped_name = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
    local player_pattern = "%[%[" .. escaped_name .. "%]%]"
    
    local player_games = {}
    
    for _, row in ipairs(rows) do
        if mw.ustring.find(row, player_pattern) then
            local cells = parse_row_cells(row)
            
            if #cells >= 17 then
                local game_type = cells[2]
                local tournament = cells[4]
                local time = cells[5]
                local result = cells[16]
                local link = cells[17]
                
                local position = nil
                local role = nil
                
                for j = 6, 15 do
                    local cell_player = clean_wikilinks(cells[j])
                    if cell_player == player_name then
                        position = j - 5
                        
                        if j >= 6 and j <= 11 then
                            role = "Мирний"
                        elseif j == 12 then
                            role = "Шериф"
                        elseif j >= 13 and j <= 14 then
                            role = "Мафія"
                        elseif j == 15 then
                            role = "Дон"
                        end
                        break
                    end
                end
                
                if position and role then
                    table.insert(player_games, {
                        type = game_type,
                        tournament = tournament,
                        time = time,
                        position = position,
                        role = role,
                        result = result,
                        link = link
                    })
                end
            end
        end
    end
    
    if #player_games == 0 then
        return "''Записів ігор не знайдено''"
    end
    
    local output = {}
    table.insert(output, '{| class="wikitable sortable" style="font-size: 14px;"')
    table.insert(output, '|-')
    table.insert(output, '! №')
    table.insert(output, '! Тип')
    table.insert(output, '! Турнір')
    table.insert(output, '! Час')
    table.insert(output, '! Позиція')
    table.insert(output, '! Роль')
    table.insert(output, '! Результат')
    table.insert(output, '! Відео')
    
    for i, game in ipairs(player_games) do
        table.insert(output, '|-')
        table.insert(output, '| style="text-align:center;" | ' .. i)
        table.insert(output, '| ' .. game.type)
        table.insert(output, '| ' .. game.tournament)
        table.insert(output, '| style="text-align:center;" | ' .. game.time)
        table.insert(output, '| style="text-align:center;" | ' .. game.position)
        
        local role_color = ""
        if game.role == "Мирний" then
            role_color = "#90EE90"
        elseif game.role == "Шериф" then
            role_color = "#4169E1"
        elseif game.role == "Мафія" then
            role_color = "#DC143C"
        elseif game.role == "Дон" then
            role_color = "#8B0000"
        end
        
        table.insert(output, '| style="background-color:' .. role_color .. '; text-align:center;" | ' .. game.role)
        
        local result_style = ""
        if game.result == "Місто" or game.result == "Мирні" then
            result_style = 'style="color:#4caf50; font-weight:bold;"'
        elseif game.result == "Мафія" then
            result_style = 'style="color:#f44336; font-weight:bold;"'
        end
        
        table.insert(output, '| ' .. result_style .. ' | ' .. game.result)
        table.insert(output, '| [' .. game.link .. ' YouTube]')
    end
    
    table.insert(output, '|}')
    
    return table.concat(output, '\n')
end

function p.games_count_records(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return "0"
    end
    
    local page_content = get_page_content("Записи_ігор")
    if not page_content then
        return "0"
    end
    
    local table_start = mw.ustring.find(page_content, "{|")
    local table_end = mw.ustring.find(page_content, "|}", table_start)
    
    if not table_start or not table_end then
        return "0"
    end
    
    local table_content = mw.ustring.sub(page_content, table_start, table_end + 1)
    local rows = parse_table_rows(table_content)
    
    local escaped_name = mw.ustring.gsub(player_name, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1")
    local player_pattern = "%[%[" .. escaped_name .. "%]%]"
    
    local count = 0
    for _, row in ipairs(rows) do
        if mw.ustring.find(row, player_pattern) then
            count = count + 1
        end
    end
    
    return tostring(count)
end

-- ================================================
-- ФУНКЦІЇ З FETCHDATA6 (season_achievements)
-- ================================================

function p.season_achievements(frame)
    local player_name = frame.args.player or frame.args[1]
    
    if not player_name or player_name == "" then
        return error_output("Не вказано ім'я гравця")
    end
    
    local season_data = {}
    local season_names = {
        "Перший сезон",
        "Другий сезон",
        "Третій сезон",
        "Четвертий сезон",
        "П'ятий сезон",
        "Шостий сезон",
        "Сьомий сезон",
        "Восьмий сезон",
        "Дев'ятий сезон"
    }
    
    for i, season_name in ipairs(season_names) do
        local content = get_page_content(season_name)
        
        if content then
            local final_section = mw.ustring.match(content, "==+%s*Фінал%s*==+(.-)\n==")
            if not final_section then
                final_section = mw.ustring.match(content, "==+%s*Фінал%s*==+(.*)")
            end
            
            if final_section then
                local table_start = mw.ustring.find(final_section, "{|")
                local table_end = mw.ustring.find(final_section, "|}", table_start)
                
                if table_start and table_end then
                    local table_content = mw.ustring.sub(final_section, table_start, table_end + 1)
                    local player_data = find_player_data(table_content, player_name)
                    
                    if player_data and #player_data >= 1 then
                        table.insert(season_data, {
                            season = i,
                            place = player_data[1] or "—"
                        })
                    end
                end
            end
        end
    end
    
    if #season_data == 0 then
        return "''Гравець не брав участі в жодному сезоні''"
    end
    
    local output = {}
    table.insert(output, '{| class="wikitable" style="text-align:center; font-size: 14px;"')
    table.insert(output, '|-')
    
    for _, data in ipairs(season_data) do
        table.insert(output, '! [[' .. season_names[data.season] .. '|' .. data.season .. ']]')
    end
    
    table.insert(output, '|-')
    
    for _, data in ipairs(season_data) do
        table.insert(output, '| ' .. data.place)
    end
    
    table.insert(output, '|}')
    
    return table.concat(output, '\n')
end

return p