```lua
local sampev = require'lib.samp.events' -- TramBot by Tectrex
local imgui = require'imgui'
local encoding = require'encoding'
encoding.default = 'CP1251'
u8 = encoding.UTF8
local function pressKey(key, duration)
    setGameKeyState(key, 255)
    wait(0)
    setGameKeyState(key, 0)
    wait(duration)
end

local TramBot = {
    isActive = false,
    station = 0,
    completedRoutes = 0,
    steps = {{'RUNFIX', -2094.7727050781, 515.27862548828, 1487.6927490234}, {'RUNFIX', -2099.2482910156, 511.8908996582, 1487.6927490234}, {'RUNHAVKA', -2097.4533691406, 512.04766845703, 1487.6927490234}, {'EAT'}, {'RUNREYS', -2102.4099121094, 512.92535400391, 1487.6927490234}, {'TAKEREYS'}},
    stationTimes = {},
    autoEat = true,
    eatAfterRoutes = 1,
    isFree = false
}

function TramBot:toggle()
    self.isActive = not self.isActive
    printStringNow('~p~Tram BOT: ' .. (self.isActive and '~G~ON' or '~r~OFF'), 2000)
    if self.isActive then self:generateStationTimes() end
end

function TramBot:generateStationTimes()
    local totalTime = 180000
    local remainingTime = totalTime
    self.stationTimes = {}
    for i = 1, 4 do
        local time = math.random(math.floor(remainingTime / (5 - i)))
        table.insert(self.stationTimes, time)
        remainingTime = remainingTime - time
    end

    table.insert(self.stationTimes, remainingTime)
end

function TramBot:handleInCar()
    local _, markerX, markerY, markerZ = self:searchMarker()
    local playerX, playerY, playerZ = getCharCoordinates(PLAYER_PED)
    local distance = getDistanceBetweenCoords3d(playerX, playerY, playerZ, markerX, markerY, markerZ)
    local car = storeCarCharIsInNoSave(PLAYER_PED)
    setTrainSpeed(car, distance > 1 and 24.0 or 0.0)
    if distance <= 1 then
        self.station = (self.station + 1) % 6
        if self.station == 5 then
            self.completedRoutes = self.completedRoutes + 1
            printStringNow('Route done. Routes completed: ' .. self.completedRoutes, 6000)
            self.station = 0
        else
            if self.station <= #self.stationTimes then
                printStringNow(string.format('Station: %d / Stop time: %d seconds', self.station, self.stationTimes[self.station] / 1000), 1000)
                wait(self.stationTimes[self.station])
            end
        end
    end
end

function TramBot:handleOnFoot()
    self.station = 0
    for _, step in ipairs(self.steps) do
        local action, x, y, z = table.unpack(step)
        if action == 'EAT' then
            if self.autoEat and self.completedRoutes % self.eatAfterRoutes == 0 then self:eat() end
        elseif action == 'TAKEREYS' then
            if self.isFree then self:takeReys() end
        else
            self:moveToCoordinates(x, y, z)
        end
    end
end

function TramBot:eat()
    pressKey(21, 300)
    sampSendDialogResponse(sampGetCurrentDialogId(), 1, 3, '')
    wait(2000)
end

function TramBot:takeReys()
    while not isCharInAnyCar(PLAYER_PED) and self.isActive do
        pressKey(21, 100)
        sampSendDialogResponse(sampGetCurrentDialogId(), 1, 0, '')
        wait(500)
    end
end

function TramBot:searchMarker()
    for id = 0, 31 do
        local markerStruct = 0xC7F168 + id * 56
        local markerPosX = representIntAsFloat(readMemory(markerStruct, 4, false))
        if markerPosX ~= 0.0 then return true, markerPosX, representIntAsFloat(readMemory(markerStruct + 4, 4, false)), representIntAsFloat(readMemory(markerStruct + 8, 4, false)) end
    end
    return false, 0.0, 0.0, 0.0
end

function TramBot:moveToCoordinates(px, py, pz)
    local step = 1.0
    local x, y, z = getCharCoordinates(PLAYER_PED)
    while getDistanceBetweenCoords3d(px, py, pz, x, y, z) > step do
        if not self.isActive then return end
        x = x + step * (px - x) / getDistanceBetweenCoords3d(px, py, pz, x, y, z)
        y = y + step * (py - y) / getDistanceBetweenCoords3d(px, py, pz, x, y, z)
        z = z + step * (pz - z) / getDistanceBetweenCoords3d(px, py, pz, x, y, z)
        setCharCoordinates(PLAYER_PED, x, y, z)
        wait(100)
    end

    setCharCoordinates(PLAYER_PED, px, py, pz)
end

local showWindow = imgui.ImBool(false)
local eatAfterRoutes = imgui.ImInt(TramBot.eatAfterRoutes)
function main()
    math.randomseed(os.clock())
    repeat
        wait(0)
    until isSampAvailable()

    sampAddChatMessage('Tram bot by Tectrex', -1)
    sampRegisterChatCommand('Trambot', function()
        showWindow.v = not showWindow.v
        imgui.Process = showWindow.v
    end)

    while true do
        wait(0)
        if TramBot.isActive then
            if isCharInAnyCar(PLAYER_PED) then
                TramBot:handleInCar()
            else
                TramBot:handleOnFoot()
            end
        end

        imgui.Process = showWindow.v
    end
end

function sampev.onSendPlayerSync(data)
    if TramBot.isActive then
        data.moveSpeed.x = -0.42
        data.moveSpeed.y = 0.81
        data.moveSpeed.z = 0
    end
end

function sampev.onSetObjectMaterialText(id, data)
    local handle = sampGetObjectHandleBySampId(id)
    local modelId = getObjectModel(handle)
    TramBot.isFree = (modelId == 19483) and string.find(data.text, 'Свободно') ~= nil
end

function imgui.OnDrawFrame()
    if showWindow.v then
        imgui.SetNextWindowSize(imgui.ImVec2(215, 530), imgui.Cond.FirstUseEver)
        imgui.Begin('TramBot By Tectrex', showWindow)
        imgui.PushStyleVar(imgui.StyleVar.FramePadding, imgui.ImVec2(10, 10))
        if imgui.Button(u8'Включить TramBot', imgui.ImVec2(200, 50)) then TramBot:toggle() end
        imgui.PopStyleVar()
        imgui.Separator()
        imgui.Text('TramBot ' .. (TramBot.isActive and u8'Активен' or u8'Неактивен'))
        if TramBot.isActive then
            imgui.Text(string.format(u8'Текущая станция: %d', TramBot.station))
            if TramBot.station < #TramBot.stationTimes then
                local waitTime = TramBot.stationTimes[TramBot.station] or 0
                imgui.Text(string.format(u8'Время ожидания: %d секунд', waitTime / 1000))
            end

            imgui.Text(string.format(u8'Рейсов сделано: %d', TramBot.completedRoutes))
        end

        imgui.Separator()
        imgui.BeginChild('ChildWindow', imgui.ImVec2(0, 200), true)
        imgui.Text(u8'Update 1.0 Релиз')
        imgui.TextWrapped(u8'Update 1.1 Бот не берет рейс, если нет свободных рейсов.')
        imgui.EndChild()
        imgui.Separator()
        imgui.Text(u8'Настройки автоеды')
        if imgui.Checkbox(u8'Включить автоеду', imgui.ImBool(TramBot.autoEat)) then TramBot.autoEat = not TramBot.autoEat end
        imgui.Text(u8'Есть через N рейсов:')
        if imgui.InputInt('', eatAfterRoutes) then TramBot.eatAfterRoutes = eatAfterRoutes.v end
        imgui.Separator()
        imgui.Text(string.format(u8'Состояние рейсов: %s', TramBot.isFree and u8'Свободно' or u8'Занято'))
        imgui.End()
    end
end

function apply_custom_style()
    local style = imgui.GetStyle()
    local colors = style.Colors
    local clr = imgui.Col
    local ImVec4 = imgui.ImVec4
    style.WindowRounding = 5.0
    style.WindowTitleAlign = imgui.ImVec2(0.5, 0.5)
    style.ChildWindowRounding = 5.0
    style.FrameRounding = 5.0
    style.ItemSpacing = imgui.ImVec2(10.0, 10.0)
    style.ScrollbarSize = 15.0
    style.ScrollbarRounding = 5.0
    style.GrabMinSize = 10.0
    style.GrabRounding = 5.0
    colors[clr.WindowBg] = ImVec4(0.1, 0.1, 0.1, 1.0)
    colors[clr.TitleBg] = ImVec4(0.2, 0.2, 0.2, 1.0)
    colors[clr.TitleBgActive] = ImVec4(0.3, 0.3, 0.3, 1.0)
    colors[clr.Button] = ImVec4(0.2, 0.2, 0.2, 1.0)
    colors[clr.ButtonHovered] = ImVec4(0.3, 0.3, 0.3, 1.0)
    colors[clr.ButtonActive] = ImVec4(0.4, 0.4, 0.4, 1.0)
    colors[clr.FrameBg] = ImVec4(0.2, 0.2, 0.2, 1.0)
    colors[clr.FrameBgHovered] = ImVec4(0.3, 0.3, 0.3, 1.0)
    colors[clr.FrameBgActive] = ImVec4(0.4, 0.4, 0.4, 1.0)
    colors[clr.CheckMark] = ImVec4(0.0, 0.5, 0.0, 1.0)
end

apply_custom_style()
```
Преобразуй следующий код, чтобы он соответствовал уровню senior-разработчика: используй объектно-ориентированное программирование с инкапсуляцией, абстракцией и наследованием там, где это уместно, сделай архитектуру модульной, расширяемой и поддерживаемой, оптимизируй производительность и читаемость, улучшай именования переменных, функций и классов, устраняй дублирование кода, применяй подходящие паттерны проектирования для гибкости и масштабируемости, внедри обработку ошибок с информативными сообщениями и обеспечь логирование ключевых событий, добавь подробные комментарии и документацию к основным классам и методам, обеспечь финальный код простым для понимания, тестирования и поддержки.