import os
from rich import print
import time
from rich.console import Console
from rich.progress import Progress, BarColumn, TextColumn, TimeRemainingColumn

console = Console()

project_directory = os.getcwd()

def create_project_structure(project_directory, directories, files):
          try:
            total_files = len(files)
            completed_files = 0
            
            with Progress(
                TextColumn("[bold blue]{task.description}[/bold blue]"),
                BarColumn(),
                TextColumn("{task.percentage:>3.0f}%"),
                TimeRemainingColumn(),
                console=console
            ) as progress:
                
                task = progress.add_task("[cyan]Creando archivos...", total=total_files)
                
                # Crear directorios
                for directory in directories:
                  try:
                    dir_path = os.path.join(project_directory, directory)
                    os.makedirs(dir_path, exist_ok=True)
                    time.sleep(0.5)  # Simula un pequeño retraso visual
                    console.clear()
                  except Exception as e:
                    console.print("error al crear el directorio")
                    
                    
                # Crear archivos y añadir contenido
                for file_dir, file_name, content in files: 
                  try:
                    file_path = os.path.join(project_directory, file_dir, file_name)
                    os.makedirs(os.path.join(project_directory, file_dir), exist_ok=True)
                    
                    with open(file_path, 'w', encoding='utf-8') as f:
                        f.write(content)
                    
                    load_time = min(0.05 * len(content), 1)  # Simula carga según contenido (máx 2s)
                    time.sleep(load_time)
                    
                    console.print(f"[bold green]Contenido adicionado a ' {file_path} '[/bold green]")
                    completed_files += 1
                    console.clear()
                    progress.update(task, advance=1)
                    
                  except Exception as e:
                    console.print("error al adicionar contenido")
                    console.print(e)
                    
                print("...")
                print(f"/*cd  {project_directory} && pip install -r requirements.txt && dcp run {project_directory}*/")
                print("...")
                    
          except Exception as e:
            console.print(e)

def bot(project_name):
  project_directory_2 = project_directory + f"/{project_name}"
  try:
     # Crear directorios principales y archivos
        os.mkdir(project_name)

        # Estructura de directorios y archivos
        directories = [
            'assets',
            'buttons',
            'commands',
            'config',
            'dcp_module',
            'functions',
            'utils',
        ]

        
        files = [
            # Assets
            ('assets', '__init__.py', ''),
            
            #commands
            ('commands', '__init__.py', ''),
            ('commands', 'ping.py', """
#/ping

import discord
from discord.ext import commands
from buttons.buttons_help import ButtonsHelp

async def ping_layout(interaction, latency):
    try:
        embed = discord.Embed(title="🏓 Pong", description=f"{interaction.user.mention} {latency} ms / Pong!", color=0x00ff00)
        embed.set_image(url="https://cusoft.tech/wp-content/uploads/2024/07/image-24.jpg")  # -> images can be attached by external url
        embed.set_footer(text="💻 Polaris", icon_url="https://cusoft.tech/wp-content/uploads/2024/05/working.gif")
        await interaction.response.send_message(embed=embed, ephemeral=True, view=ButtonsHelp()) # -> view=ButtonsHelp() to display the buttons
    except Exception as e:
        raise Exception("This is a test exception")
    
    
    
    
             """),
            
            
            # Monitor
            ('dcp_module/monitor', '__init__.py', ''),
            ('dcp_module/monitor/client', '__init__.py', ''),
            ('dcp_module/monitor/client', 'index.html', """
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Discord Monitor</title>
    <link rel="stylesheet" href="css/style.css" />
    <link rel="icon" href="./media/ex-1.jpg" />
  </head>
  <body>
    <div class="container">
      <nav class="navbar" style="overflow: auto">
        <h2>Monitor</h2>
        <ul>
          <span>V2 Completed</span>
          <li style="margin-top: 10px" data-page="logs" class="active">
            Bot Logs
          </li>
          <li data-page="components">Components</li>
          <li data-page="network">Network</li>
          <li style="margin-bottom: 50px" data-page="testing">Testing</li>

          <span>V3 Working ...</span>
          <li style="margin-top: 10px" data-page="simulator">Simulator</li>
          <li style="margin-bottom: 50px" data-page="performance">
            Performance
          </li>

          <span>Documentation</span>
          <li style="margin-top: 10px" data-page="tutorials">Tutorials</li>
          <li data-page="documentation">Documentation</li>
        </ul>

        <div
          style="
            display: flex;
            gap: 1rem;
            justify-content: center;
            align-items: center;
            margin: 15px 0px 5px 0px;
          "
        >
          <a href="https://x.com/MrWiki15_">
            <img src="./media/icons/twitter.svg" />
          </a>

          <a href="https://github.com/MrWiki15/discord-package">
            <img src="./media/icons/github.svg" />
          </a>
        </div>
      </nav>
      <main class="main-content">
        <iframe id="contentFrame" src="pages/logs.html"></iframe>
      </main>
    </div>
    <script src="js/main.js"></script>
  </body>
</html>



"""),
            
            #css monitor
            ('dcp_module/monitor/client/css', '__init__.py', ''),
            ('dcp_module/monitor/client/css', 'style.css', """
/* ====== Reset y Configuración Básica ====== */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
  background-color: #36393f;
  color: #dcddde;
  line-height: 1.6;
  transition: background-color 0.3s, color 0.3s;
}

.container {
  display: flex;
  min-height: 100vh;
}

/* ====== Navbar ====== */
.navbar {
  width: 200px;
  background-color: #202225;
  color: #dcddde;
  padding: 20px;
  height: 100vh;
  position: fixed;
  transition: width 0.3s ease;
}

.navbar h2 {
  font-size: 24px;
  margin-bottom: 30px;
  text-align: center;
}

.navbar ul {
  list-style-type: none;
}

.navbar ul span {
  padding: 5px 10px;
  background-color: #141414;
  border-radius: 5px;
  color: #f7f7f760;
  font-size: 10px;
  margin-bottom: 10px !important;
}

.navbar li {
  padding: 12px 15px;
  margin-bottom: 10px;
  cursor: pointer;
  border-radius: 5px;
  transition: background-color 0.3s;
}

.navbar li:hover,
.navbar li.active {
  background-color: #393c43;
}

/* ====== Botón Dark Mode ====== */
#darkModeToggle {
  width: 100%;
  padding: 10px;
  margin-top: 20px;
  background-color: #7289da;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  transition: background-color 0.3s;
}

#darkModeToggle:hover {
  background-color: #677bc4;
}

/* ====== Main Content ====== */
.main-content {
  flex-grow: 1;
  margin-left: 250px;
  transition: margin-left 0.3s ease;
}

.section {
  background-color: #2f3136;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
  padding: 20px;
}

.section h1 {
  font-size: 28px;
  margin-bottom: 20px;
  color: #ffffff;
}

/* ====== Filtros ====== */
.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 20px;
}

.filters input,
.filters button {
  padding: 10px 15px;
  border: 1px solid #ddd;
  border-radius: 5px;
  font-size: 14px;
}

.filters input:focus {
  outline: none;
  border-color: #7289da;
}

.filters button {
  background-color: #7289da;
  color: white;
  border: none;
  cursor: pointer;
  transition: background-color 0.3s;
}

.filters button:hover {
  background-color: #677bc4;
}

/* ====== Tabla ====== */
table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0 10px;
}

th,
td {
  padding: 15px;
  text-align: left;
}

th {
  background-color: #202225;
  color: #ffffff;
  font-weight: bold;
}

tbody tr {
  background-color: #36393f;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  transition: transform 0.2s, box-shadow 0.2s;
}

tbody tr:hover {
  transform: translateY(-3px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}

/* ====== Modo Oscuro ====== */
body.dark-mode {
  background-color: #202225;
}

body.dark-mode .navbar {
  background-color: #2f3136;
}

body.dark-mode .section {
  background-color: #36393f;
}

body.dark-mode table th {
  background-color: #202225;
}

body.dark-mode tbody tr {
  background-color: #2f3136;
}

body.dark-mode .filters input {
  background-color: #40444b;
  color: #dcddde;
  border-color: #202225;
}

/* ====== Responsive Design ====== */
@media (max-width: 768px) {
  .navbar {
    width: 200px;
    height: auto;
    position: static;
  }

  .main-content {
    margin-left: 50px;
  }

  .filters {
    flex-direction: column;
  }
}

/* ====== Estilo del iframe ====== */
#contentFrame {
  width: calc(100% + 50px);
  height: 100vh;
  border: none;
  margin-left: -50px;
}

/* Estilo para ocultar las barras de desplazamiento en diferentes navegadores */
body,
nav {
  overflow: hidden; /* Oculta las barras de desplazamiento */
}

body::-webkit-scrollbar,
nav::-webkit-scrollbar {
  display: none; /* Oculta las barras de desplazamiento en navegadores basados en WebKit */
}

body,
nav {
  -ms-overflow-style: none; /* IE y Edge */
  scrollbar-width: none; /* Firefox */
}

"""),            
            ('dcp_module/monitor/client/css', 'logs.css', """
/* Estilos para el modal */
.modal {
  display: none;
  position: fixed;
  z-index: 1;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: auto;
  background-color: rgba(0, 0, 0, 0.4);

  h2,
  h3,
  p {
    color: #000;
  }
}

.modal-content {
  background-color: #fefefe;
  margin: 15% auto;
  padding: 20px;
  border: 1px solid #888;
  width: 80%;
  max-width: 600px;
  border-radius: 5px;
}

.close {
  color: #111111;
  float: right;
  font-size: 28px;
  font-weight: bold;
}

.close:hover,
.close:focus {
  color: black;
  text-decoration: none;
  cursor: pointer;
}

#logDetails {
  margin-top: 20px;
}

#logDetails h3 {
  margin-top: 15px;
  margin-bottom: 5px;
}

#logDetails pre {
  background-color: #393939;
  padding: 10px;
  border-radius: 5px;
  white-space: pre-wrap;
  word-wrap: break-word;
}

/* Estilos para modo oscuro */
body.dark-mode .modal-content {
  background-color: #2c2c2c;
  color: #141414;
}

body.dark-mode .close {
  color: #1b1b1b;
}

body.dark-mode #logDetails pre {
  background-color: #1c1c1c;
  color: #e5e5e5;
}


             """),
            ('dcp_module/monitor/client/css', 'simulator.css', """
:root {
  --blurple: #5865f2;
  --green: #57f287;
  --dark-gray: #2c2f33;
  --light-gray: #99aab5;
  --white: #ffffff;
}

.discord-simulator {
  display: flex;
  height: 100vh;
  font-family: "Whitney", "Helvetica Neue", Helvetica, Arial, sans-serif;
}

#setup {
  display: flex;
  position: absolute;
  height: 100%;
  width: 500%;
  top: 0;
  left: 0;
  transition: transform 0.5s ease-in-out;
}

.page_setup {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100vw;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.85);
}

input[type="text"] {
  width: 80%;
  padding: 10px;
  margin: 10px 0;
  border: 2px solid var(--blurple);
  border-radius: 5px;
  background-color: var(--dark-gray);
  color: var(--white);
  font-size: 16px;
  outline: none;
  transition: border-color 0.3s;
}

input[type="text"]:focus {
  border-color: var(--green);
}

input[type="button"] {
  padding: 10px 20px;
  margin-top: 10px;
  border: none;
  border-radius: 5px;
  background-color: var(--blurple);
  color: var(--white);
  font-size: 16px;
  cursor: pointer;
  transition: background-color 0.3s;
}

input[type="button"]:hover {
  background-color: var(--green);
}

@keyframes slideIn {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(0);
  }
}

.server-list {
  width: 150px;
  background-color: #2f3136;
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 10px;

  button {
    width: 68px;
    height: 28px;
    border-radius: 3%;
    background-color: #36393f;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border: none;
    border-radius: 8px;
    color: wheat;
    transition: border-radius 0.3s ease;
  }
}

.channel-list {
  width: 240px;
  background-color: #2f3136;
  padding: 20px;
  color: #8e9297;
}

.channel-list h2 {
  color: #fff;
  margin-bottom: 20px;
}

.channel-list h3 {
  text-transform: uppercase;
  font-size: 12px;
  margin-top: 20px;
  margin-bottom: 10px;
}

.channel-list ul {
  list-style-type: none;
  padding: 0;
}

.channel-list li {
  padding: 5px 0;
  cursor: pointer;
}

.channel-list li.active,
.channel-list li:hover {
  color: #dcddde;
}

.chat-area {
  flex-grow: 1;
  background-color: #36393f;
  display: flex;
  flex-direction: column;
}

.chat-header {
  padding: 15px;
  border-bottom: 1px solid #202225;
}

.chat-header h2 {
  color: #fff;
}

.messages {
  flex-grow: 1;
  padding: 20px;
  overflow-y: auto;
}

.message-input {
  padding: 20px;
  display: flex;
}

.message-input input {
  flex-grow: 1;
  background-color: #40444b;
  border: none;
  padding: 10px;
  border-radius: 5px;
  color: #dcddde;
}

.message-input button {
  background-color: #7289da;
  color: #fff;
  border: none;
  padding: 10px 15px;
  margin-left: 10px;
  border-radius: 5px;
  cursor: pointer;
}

.member-list {
  width: 200px;
  background-color: #2f3136;
  padding: 20px;
  color: #8e9297;
}

.member-list h3 {
  text-transform: uppercase;
  font-size: 12px;
  margin-bottom: 10px;
}

.member-list ul {
  list-style-type: none;
  padding: 0;
}

.member-list li {
  padding: 5px 0;
}

/* Estilos para modo oscuro */
body.dark-mode .discord-simulator {
  background-color: #202225;
}

body.dark-mode .server-list {
  background-color: #1e1f22;
}

body.dark-mode .channel-list,
body.dark-mode .member-list {
  background-color: #2b2d31;
}

body.dark-mode .chat-area {
  background-color: #313338;
}

body.dark-mode .message-input input {
  background-color: #383a40;
}

.connect-simulator {
  margin: 10px;
  text-align: center;
}

#connectButton {
  background-color: #7289da;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-weight: bold;
  transition: background-color 0.3s ease;
}

#connectButton:hover {
  background-color: #677bc4;
}

.message {
  padding: 5px 10px;
  margin: 5px 0;
  background-color: #f1f1f100;
  border-radius: 5px;
  max-width: 90%;
  word-wrap: break-word;
}

.config-section {
  margin: 10px;
  padding: 10px;
  background-color: #f4f4f4;
  border-radius: 8px;
  display: flex;
  gap: 10px;
}

.config-section input {
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 5px;
}

#connectButton {
  background-color: #7289da;
  color: white;
  padding: 10px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

#connectButton:hover {
  background-color: #5b6eae;
}

.message {
  padding: 5px 10px;
  background-color: #f0f0f014;
  border-radius: 5px;
  margin: 5px 0;
}


             """),            
            ('dcp_module/monitor/client/css', 'components.css', """
:root {
  --background-primary: #36393f;
  --background-secondary: #2f3136;
  --background-tertiary: #202225;
  --text-normal: #dcddde;
  --text-muted: #72767d;
  --header-primary: #fff;
  --interactive-normal: #b9bbbe;
  --interactive-hover: #dcddde;
  --interactive-active: #fff;
  --scrollbar-thin-thumb: #202225;
  --scrollbar-thin-track: transparent;
  --transition-duration: 0.2s;
}

body {
  font-family: "Roboto", sans-serif;
  background-color: var(--background-tertiary);
  color: var(--text-normal);
  margin: 0;
  padding: 0;
  overflow: hidden;
}

.c-c {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.header {
  padding: 20px;
  font-size: 18px;
  font-weight: 700;
  color: var(--header-primary);
  background-color: var(--background-secondary);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

#treeCanvas {
  flex-grow: 1;
  background-color: var(--background-primary);
}

.loading-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(32, 34, 37, 0.8);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.loading-spinner {
  width: 50px;
  height: 50px;
  border: 5px solid #7289da;
  border-top: 5px solid transparent;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

             """),
            ('dcp_module/monitor/client/css', 'deploy.css', """
             
             """),            
            ('dcp_module/monitor/client/css', 'documentations.css', """
             
             """),            
            ('dcp_module/monitor/client/css', 'network.css', """
:root {
  --background-primary: #36393f;
  --background-secondary: #2f3136;
  --background-tertiary: #202225;
  --text-normal: #dcddde;
  --text-muted: #72767d;
  --header-primary: #fff;
  --interactive-normal: #b9bbbe;
  --interactive-hover: #dcddde;
  --interactive-active: #fff;
  --scrollbar-thin-thumb: #202225;
  --scrollbar-thin-track: transparent;
  --transition-duration: 0.2s;
}

body {
  font-family: "Roboto", sans-serif;
  background-color: var(--background-tertiary);
  color: var(--text-normal);
  margin: 0;
  padding: 0;
}

.c-c {
  display: flex;
  width: 100%;
  height: 100vh;
}

.c-c1,
.c-c2 {
  display: flex;
  flex-direction: column;
  height: 100%;
  overflow: hidden;
}

.c-c1 {
  width: 30%;
  background-color: var(--background-secondary);
  border-right: 1px solid var(--background-tertiary);
}

.c-c2 {
  width: 70%;
  background-color: var(--background-primary);
  overflow-y: auto;
}

.header {
  padding: 20px;
  font-size: 16px;
  font-weight: 700;
  color: var(--header-primary);
  background-color: var(--background-tertiary);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

#request-list {
  overflow-y: auto;
}

.request-item {
  padding: 10px 20px;
  cursor: pointer;
  transition: background-color var(--transition-duration) ease;
  border-radius: 4px;
  margin: 5px 10px;
}

.request-item:hover {
  background-color: var(--background-primary);
}

.request-item.active {
  background-color: var(--background-tertiary);
}

#details-content {
  padding: 20px;
}

.detail-section {
  background-color: var(--background-secondary);
  border-radius: 8px;
  padding: 15px;
  margin-bottom: 20px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
  transition: transform var(--transition-duration) ease,
    box-shadow var(--transition-duration) ease;
  opacity: 0;
  transform: translateY(20px);
}

.detail-section.visible {
  opacity: 1;
  transform: translateY(0);
}

.detail-section:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}

.detail-section h3 {
  margin-bottom: 10px;
  color: var(--header-primary);
  border-bottom: 1px solid var(--interactive-normal);
  padding-bottom: 5px;
  background: linear-gradient(90deg, #7289da, #ffffff);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

.detail-item {
  margin-bottom: 5px;
  display: flex;
  justify-content: space-between;
}

.detail-item strong {
  color: var(--interactive-normal);
}

.detail-item span {
  color: var(--text-normal);
}

.animated-text {
  display: inline-block;
}

@keyframes wave {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-5px);
  }
}

.wave-animation {
  display: inline-block;
}

.important-text {
  font-size: 18px;
  font-weight: bold;
  text-align: center;
  margin: 20px 0;
  background: linear-gradient(90deg, #6feb84, #48c665, #008e09);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: gradient-shift 5s linear infinite;
}

@keyframes gradient-shift {
  0% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0% 50%;
  }
}

.loading-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(32, 34, 37, 0.8);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 1000;
}

.loading-spinner {
  width: 50px;
  height: 50px;
  border: 5px solid #7289da;
  border-top: 5px solid transparent;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

/* Scrollbar Styles */
::-webkit-scrollbar {
  width: 8px;
}

::-webkit-scrollbar-track {
  background: var(--scrollbar-thin-track);
}

::-webkit-scrollbar-thumb {
  background-color: var(--scrollbar-thin-thumb);
  border-radius: 4px;
}

/* Responsive design */
@media (max-width: 768px) {
  .c-c {
    flex-direction: column;
  }
  .c-c1,
  .c-c2 {
    width: 100%;
    height: 50vh;
  }
}

             """),            
            ('dcp_module/monitor/client/css', 'performance.css', """
body {
  font-family: "Arial", sans-serif;
  background-color: #1e1f22;
  color: white;
  text-align: center;
  padding: 20px;
}

.container {
  max-width: 800px;
  margin: auto;
}

.stats-container {
  display: flex;
  justify-content: space-around;
  flex-wrap: wrap;
  margin-bottom: 30px;
  width: 100%;
  gap: 20px;
  height: 100%;
}

.stat-card {
  background: #2c2f33;
  padding: 15px;
  border-radius: 8px;
  width: 45%;
  margin-bottom: 10px;
  box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.3);
}

.stat-card h2 {
  font-size: 16px;
  color: #99aab5;
}

.stat-card p {
  font-size: 20px;
  font-weight: bold;
  color: #ffffff;
}

             """),            
            ('dcp_module/monitor/client/css', 'security.css', """
             
             """),         
            ('dcp_module/monitor/client/css', 'testing.css', """
/* Estilos generales */
body {
  font-family: "Arial", sans-serif;
  background-color: #36393f;
  color: #dcddde;
  margin: 0;
  padding: 0;
}

.container {
  display: flex;
  height: 100vh;
}

.test-list {
  width: 30%;
  background-color: #2f3136;
  padding: 20px;
  box-sizing: border-box;
}

.test-list h2 {
  text-align: center;
  color: #dcddde;
}

.test-list ul {
  list-style-type: none;
  padding: 0;
}

.test-list li {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
  padding: 10px;
  background-color: #202225;
  border-radius: 5px;
}

.test-list li:hover {
  background-color: #7289da;
}

.test-list .run-btn {
  background-color: #7289da;
  border: none;
  color: #fff;
  padding: 5px 10px;
  border-radius: 5px;
  cursor: pointer;
}

.test-list .run-btn:hover {
  background-color: #99aab5;
}

/* Estilos del resultado */
.test-result {
  width: 70%;
  padding: 20px;
  box-sizing: border-box;
}

.test-result h2 {
  text-align: center;
  color: #dcddde;
}

#result-output {
  background-color: #2f3136;
  color: #dcddde;
  padding: 10px;
  border-radius: 5px;
  white-space: pre-wrap;
  word-wrap: break-word;
  height: calc(100vh - 100px);
  overflow-y: auto;
}

/* Estilos del loader */
.loader {
  display: none;
  border: 4px solid #f3f3f3;
  border-top: 4px solid #7289da;
  border-radius: 50%;
  width: 50px;
  height: 50px;
  animation: spin 2s linear infinite;
  margin: 20px auto;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

             """),            
            ('dcp_module/monitor/client/css', 'tutorials.css', """
             
             """),
            
            #html monitor
            ('dcp_module/monitor/client/pages', '__init__.py', ""),
            ('dcp_module/monitor/client/pages', 'components.html', """
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Components Visualization</title>
    <link rel="stylesheet" href="../css/components.css" />
    <link
      href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap"
      rel="stylesheet"
    />
  </head>
  <body>
    <div id="c-c" class="c-c">
      <div class="header">Components Visualization</div>
      <canvas id="treeCanvas"></canvas>
    </div>
    <div id="loading-overlay" class="loading-overlay">
      <div class="loading-spinner"></div>
    </div>
    <script src="../js/components.js"></script>
  </body>
</html>

             """),
            ('dcp_module/monitor/client/pages', 'deploy.html', """
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Deploy</title>
    <link rel="stylesheet" href="../css/deploy.css" />
    <link rel="stylesheet" href="../css/style.css" />
  </head>
  <body>
    <div id="c-c">Deploy</div>
    <script src="../js/deploy.js"></script>
  </body>
</html>

             """),
            ('dcp_module/monitor/client/pages', 'documentation.html', """
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Documentation</title>
    <link rel="stylesheet" href="../css/documentation.css" />
    <link rel="stylesheet" href="../css/style.css" />
  </head>
  <body style="position: relative">
    <div
      style="
        position: absolute;
        background-color: rgba(0, 0, 0, 0.9);
        top: 1;
        left: 1;
        width: 100vw;
        height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
      "
    >
      Comming in v3.0 ...
    </div>
    <div id="c-c">Documentation</div>
    <script src="../js/documentation.js"></script>
  </body>
</html>

             """),
            ('dcp_module/monitor/client/pages', 'logs.html', """
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Logs</title>
    <link rel="stylesheet" href="../css/style.css" />
    <link rel="stylesheet" href="../css/logs.css" />
  </head>
  <body>
    <div class="section" style="border-radius: 0px">
      <h1>Logs</h1>
      <div class="filters">
        <input
          type="text"
          id="filterGuild"
          placeholder="Filter by server name"
        />
        <input type="text" id="filterUser" placeholder="Filter by user name" />
        <input type="date" id="filterDate" />
        <button onclick="applyFilters()">Apply Filters</button>
        <button onclick="resetFilters()">Reset Filters</button>
      </div>
      <table id="logsTable">
        <thead>
          <tr>
            <th>Timestamp</th>
            <th>User</th>
            <th>Server</th>
            <th>Channel</th>
            <th>Message</th>
          </tr>
        </thead>
        <tbody id="logsBody"></tbody>
      </table>
    </div>

    <!-- Modal para detalles del log -->
    <div id="logModal" class="modal">
      <div class="modal-content">
        <span class="close">&times;</span>
        <h2>Log Details</h2>
        <div id="logDetails"></div>
      </div>
    </div>

    <script src="../js/logs.js"></script>
  </body>
</html>

             """),
            ('dcp_module/monitor/client/pages', 'network.html', """
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Discord-style Network Viewer</title>
    <link rel="stylesheet" href="../css/network.css" />
    <link rel="stylesheet" href="../css/style.css" />
    <link
      href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap"
      rel="stylesheet"
    />
  </head>
  <body>
    <div id="c-c" class="c-c">
      <div class="c-c1" id="request-list">
        <div class="header">Network Requests</div>
      </div>
      <div class="c-c2" id="request-details">
        <div class="header">Request Details</div>
        <div id="details-content"></div>
      </div>
    </div>
    <div id="loading-overlay" class="loading-overlay">
      <div class="loading-spinner"></div>
    </div>
    <script src="../js/network.js"></script>
  </body>
</html>

             """),
            ('dcp_module/monitor/client/pages', 'performance.html', """
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Performance Monitor</title>
    <link rel="stylesheet" href="../css/performance.css" />
    <link rel="stylesheet" href="../css/style.css" />
  </head>
  <body style="position: relative; padding: 0px">
    <div
      style="
        position: absolute;
        background-color: rgba(0, 0, 0, 0.9);
        top: 1;
        left: 1;
        width: 100vw;
        height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
      "
    >
      Comming in v3.0 ...
    </div>
    <div class="container">
      <div class="stats-container">
        <div class="stat-card">
          <h2>RAM Usage</h2>
          <p><span id="ram-usage">Loading...</span> MB</p>
        </div>
        <div class="stat-card">
          <h2>Flask Server RAM</h2>
          <p><span id="flask-ram">Loading...</span> MB</p>
        </div>
        <div class="stat-card">
          <h2>Estimated Production RAM</h2>
          <p><span id="prod-ram">Loading...</span> MB</p>
        </div>
        <div class="stat-card">
          <h2>Estimated Cost</h2>
          <p>$<span id="cost">Loading...</span>/ Month</p>
        </div>
      </div>
    </div>

    <script src="../js/performance.js"></script>
  </body>
</html>

             """),
            ('dcp_module/monitor/client/pages', 'security.html', """
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Security</title>
    <link rel="stylesheet" href="../css/security.css" />
    <link rel="stylesheet" href="../css/style.css" />
  </head>
  <body>
    <div id="c-c">Testing</div>
    <script src="../js/security.js"></script>
  </body>
</html>

             """),
            ('dcp_module/monitor/client/pages', 'simulator.html', """
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Discord Simulator (API)</title>
    <link rel="stylesheet" href="../css/style.css" />
    <link rel="stylesheet" href="../css/simulator.css" />
  </head>
  <body>
    <div class="discord-simulator">
      <div
        style="
          position: absolute;
          background-color: rgba(0, 0, 0, 0.9);
          top: 1;
          left: 1;
          width: 100vw;
          height: 100vh;
          display: flex;
          justify-content: center;
          align-items: center;
        "
      >
        Comming in v3.0 ...
      </div>
      <!-- Configuración 
      <div id="setup">
        <div class="page_setup" id="step1">
          <span>Para usar el simulador, se necesita más información</span>
          <input
            type="button"
            value="Realizar configuración"
            onclick="showNextStep(2)"
          />
        </div>

        <div class="page_setup" id="step2">
          <span>Insertar Bot Token</span>
          <input type="text" id="botToken" />
          <input
            type="button"
            value="Siguiente"
            onclick="saveAndNext('botToken', 3)"
          />
        </div>

        <div class="page_setup" id="step3">
          <span>Insertar Bot Client Id</span>
          <input type="text" id="clientId" />
          <input
            type="button"
            value="Siguiente"
            onclick="saveAndNext('clientId', 4)"
          />
        </div>

        <div class="page_setup" id="step4">
          <span>Insertar Server Id</span>
          <input type="text" id="serverId" />
          <input
            type="button"
            value="Siguiente"
            onclick="saveAndNext('serverId', 5)"
          />
        </div>

        <div class="page_setup" id="step5">
          <span>Insertar Channel Id</span>
          <input type="text" id="channelId" />
          <input
            type="button"
            value="Finalizar"
            onclick="saveAndFinish('channelId')"
          />
        </div>
      </div> -->

      <div class="server-list">
        <h3>Comandos</h3>
        <div id="commandList" class=""></div>
      </div>

      <div class="chat-area">
        <div class="chat-header"><h2># general</h2></div>
        <div class="messages" id="messageArea"></div>
        <div class="message-input">
          <input type="text" id="messageInput" placeholder="Message #general" />
          <button id="sendButton">Send</button>
        </div>
      </div>

      <!-- Lista de Miembros -->
      <div class="member-list">
        <h3>Miembros en Línea</h3>
        <ul id="memberList"></ul>
      </div>
    </div>

    <script src="../js/simulator.js"></script>
  </body>
</html>

             """),
            ('dcp_module/monitor/client/pages', 'testing.html', """
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Testing</title>
    <link rel="stylesheet" href="../css/testing.css" />
    <link rel="stylesheet" href="../css/style.css" />
  </head>
  <body>
    <div class="container">
      <div class="test-list">
        <h2>Lista de Tests</h2>
        <ul>
          <li>
            <span>Message Test</span>
            <button class="run-btn" data-test-id="3">Play</button>
          </li>
          <li>
            <span>Command Test</span>
            <button class="run-btn" data-test-id="1">Play</button>
          </li>
          <li>
            <span>Command 404 Test</span>
            <button class="run-btn" data-test-id="2">Play</button>
          </li>
        </ul>
      </div>
      <div class="test-result">
        <h2>Resultado del Test</h2>
        <pre id="result-output">Selecciona un test para ver el resultado.</pre>
        <div id="loader" class="loader"></div>
      </div>
    </div>
    <script src="../js/testing.js"></script>
  </body>
</html>

             """),
            ('dcp_module/monitor/client/pages', 'tutorials.html', """
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Tutorials</title>
    <link rel="stylesheet" href="../css/tutorials.css" />
    <link rel="stylesheet" href="../css/style.css" />
  </head>
  <body style="position: relative">
    <div
      style="
        position: absolute;
        background-color: rgba(0, 0, 0, 0.9);
        top: 1;
        left: 1;
        width: 100vw;
        height: 100vh;
        display: flex;
        justify-content: center;
        align-items: center;
      "
    >
      Comming in v3.0 ...
    </div>
    <div id="c-c">Tutorials</div>
    <script src="../js/tutorials.js"></script>
  </body>
</html>

             """),
            
            #js monitor
            ('dcp_module/monitor/client/js', '__init__.py', ""),
            ('dcp_module/monitor/client/js', 'components.js', """
document.addEventListener("DOMContentLoaded", () => {
  const canvas = document.getElementById("treeCanvas");
  const ctx = canvas.getContext("2d");
  const loadingOverlay = document.getElementById("loading-overlay");

  let treeData;
  const canvasOffset = { x: 0, y: 0 };
  let isDragging = false;
  let lastMousePos = { x: 0, y: 0 };
  let zoom = 1;
  let hoveredNode = null;
  let nodes = [];

  function resizeCanvas() {
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight - 60;
  }

  window.addEventListener("resize", resizeCanvas);
  resizeCanvas();

  async function fetchTreeData() {
    try {
      loadingOverlay.style.display = "flex";
      const response = await fetch(
        "http://127.0.0.1:8000/logs/components.json"
      );
      treeData = await response.json();
      drawTree();
    } catch (error) {
      console.error("Error fetching tree data:", error);
    } finally {
      loadingOverlay.style.display = "none";
    }
  }

  function drawTree() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.save();
    ctx.translate(canvasOffset.x, canvasOffset.y);
    ctx.scale(zoom, zoom);

    nodes = [];
    const totalWidth = calculateTreeWidth(treeData);
    drawNode(treeData, canvas.width / 2, 50, totalWidth, 0);
    ctx.restore();
  }

  function calculateTreeWidth(node) {
    if (!node.children || node.children.length === 0) {
      return 150;
    }

    const childrenWidth = node.children.reduce(
      (sum, child) => sum + calculateTreeWidth(child),
      0
    );
    return Math.max(150, childrenWidth);
  }

  function drawNode(node, x, y, width, depth) {
    const nodeHeight = 40;
    const verticalSpacing = 300;
    const horizontalSpacing = 100;
    const horizontalPadding = 80;

    const scale = node === hoveredNode ? 1.2 : 1;
    const nodeWidth =
      Math.min(ctx.measureText(node.name).width + 40, 150) * scale;

    // Store node information for hit detection
    nodes.push({
      x: x - nodeWidth / 2,
      y: y,
      width: nodeWidth,
      height: nodeHeight,
      name: node.name,
      type: node.type,
      path: node.path || "",
    });

    // Draw node with animation if hovered
    ctx.save();
    ctx.translate(x, y + nodeHeight / 2);
    ctx.scale(scale, scale);
    ctx.translate(-x, -(y + nodeHeight / 2));

    ctx.fillStyle = node.type === "directory" ? "#7289da" : "#43b581";
    ctx.beginPath();
    ctx.roundRect(x - nodeWidth / 2, y, nodeWidth, nodeHeight, 15);
    ctx.fill();

    // Draw text
    ctx.fillStyle = "#ffffff";
    ctx.font = "14px Roboto";
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.fillText(node.name, x, y + nodeHeight / 2);

    ctx.restore();

    if (node.children && node.children.length > 0) {
      let startX = x - width / 2 + horizontalPadding;
      node.children.forEach((child, index) => {
        const childWidth = calculateTreeWidth(child);
        const childX = startX + childWidth / 2;
        const childY = y + verticalSpacing;

        drawAnimatedBranch(x, y + nodeHeight, childX, childY);
        drawNode(child, childX, childY, childWidth, depth + 1);

        startX += childWidth + horizontalPadding;
      });
    }
  }

  function drawAnimatedBranch(startX, startY, endX, endY) {
    const controlPoint1X = startX;
    const controlPoint1Y = startY + (endY - startY) / 2;
    const controlPoint2X = endX;
    const controlPoint2Y = startY + (endY - startY) / 2;

    ctx.strokeStyle = "#72767d";
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.moveTo(startX, startY);

    const time = Date.now() * 0.001;
    const waveAmplitude = 5;
    const waveFrequency = 0.05;

    for (let t = 0; t <= 1; t += 0.01) {
      const x =
        Math.pow(1 - t, 3) * startX +
        3 * Math.pow(1 - t, 2) * t * controlPoint1X +
        3 * (1 - t) * Math.pow(t, 2) * controlPoint2X +
        Math.pow(t, 3) * endX;

      const y =
        Math.pow(1 - t, 3) * startY +
        3 * Math.pow(1 - t, 2) * t * controlPoint1Y +
        3 * (1 - t) * Math.pow(t, 2) * controlPoint2Y +
        Math.pow(t, 3) * endY;

      const waveOffset =
        Math.sin(t * 10 + time * 2) * waveAmplitude * Math.sin(t * Math.PI);

      ctx.lineTo(
        x + waveOffset * Math.cos(waveFrequency * y),
        y + waveOffset * Math.sin(waveFrequency * y)
      );
    }

    ctx.stroke();
  }

  canvas.addEventListener("mousemove", (e) => {
    const rect = canvas.getBoundingClientRect();
    const mouseX = (e.clientX - rect.left - canvasOffset.x) / zoom;
    const mouseY = (e.clientY - rect.top - canvasOffset.y) / zoom;

    const hoveredNodeNew = nodes.find(
      (node) =>
        mouseX >= node.x &&
        mouseX <= node.x + node.width &&
        mouseY >= node.y &&
        mouseY <= node.y + node.height
    );

    if (hoveredNodeNew !== hoveredNode) {
      hoveredNode = hoveredNodeNew;
      drawTree();
    }

    if (isDragging) {
      const dx = e.clientX - lastMousePos.x;
      const dy = e.clientY - lastMousePos.y;
      canvasOffset.x += dx;
      canvasOffset.y += dy;
      lastMousePos = { x: e.clientX, y: e.clientY };
      drawTree();
    }
  });

  canvas.addEventListener("mousedown", (e) => {
    isDragging = true;
    lastMousePos = { x: e.clientX, y: e.clientY };
  });

  canvas.addEventListener("mouseup", (e) => {
    isDragging = false;
    if (hoveredNode && hoveredNode.type === "file") {
      openInVSCode(hoveredNode.path);
    }
  });

  canvas.addEventListener("wheel", (e) => {
    e.preventDefault();
    const zoomSpeed = 0.1;
    const zoomPoint = {
      x: e.clientX - canvasOffset.x,
      y: e.clientY - canvasOffset.y,
    };

    if (e.deltaY < 0) {
      zoom *= 1 + zoomSpeed;
    } else {
      zoom /= 1 + zoomSpeed;
    }

    canvasOffset.x = e.clientX - zoomPoint.x * zoom;
    canvasOffset.y = e.clientY - zoomPoint.y * zoom;

    drawTree();
  });

  function openInVSCode(filePath) {
    fetch(
      `http://127.0.0.1:8000/open-in-vscode?file=${encodeURIComponent(
        filePath
      )}`,
      {
        method: "POST",
      }
    )
      .then((response) => response.json())
      .then((data) => {
        if (data.success) {
          console.log("File opened in VS Code");
        } else {
          console.error("Failed to open file in VS Code:", data.error);
        }
      })
      .catch((error) => {
        console.error("Error opening file in VS Code:", error);
      });
  }

  fetchTreeData();
  setInterval(drawTree, 50);
});

             """),
            ('dcp_module/monitor/client/js', 'deploy.js', """
             
             """),
            ('dcp_module/monitor/client/js', 'documentation.js', """"""),
            ('dcp_module/monitor/client/js', 'logs.js', """
let allLogs = [];
const modal = document.getElementById("logModal");
const span = document.getElementsByClassName("close")[0];

async function loadLogs() {
  try {
    const response = await fetch("../../../logs/logs.json");
    allLogs = await response.json();
    applyFilters();
  } catch (error) {
    console.error("Error loading logs:", error);
  }
}

function applyFilters() {
  const filterGuild = document
    .getElementById("filterGuild")
    .value.toLowerCase();
  const filterUser = document.getElementById("filterUser").value.toLowerCase();
  const filterDate = document.getElementById("filterDate").value;

  const filteredLogs = allLogs.filter((log) => {
    return (
      (!filterGuild || log.guild.name.toLowerCase().includes(filterGuild)) &&
      (!filterUser || log.user.name.toLowerCase().includes(filterUser)) &&
      (!filterDate || log.timestamp.startsWith(filterDate))
    );
  });

  updateTable(filteredLogs);
}

function resetFilters() {
  document.getElementById("filterGuild").value = "";
  document.getElementById("filterUser").value = "";
  document.getElementById("filterDate").value = "";
  applyFilters();
}

function updateTable(logs) {
  const logsBody = document.getElementById("logsBody");
  logsBody.innerHTML = "";

  logs.forEach((log, index) => {
    const row = document.createElement("tr");
    row.innerHTML = `
            <td>${log.timestamp}</td>
            <td>${log.user.name}</td>
            <td>${log.guild ? log.guild.name : "N/A"}</td >
            <td>${log.channel.name}</td> 
            <td>${log.message}</td>
        `;
    row.addEventListener("click", () => showLogDetails(log));
    logsBody.appendChild(row);
  });
}

function showLogDetails(log) {
  const logDetails = document.getElementById("logDetails");
  logDetails.innerHTML = `
        <h3>Timestamp</h3>
        <p>${log.timestamp}</p>
        
        <h3>Message</h3>
        <p>${log.message}</p>
        
        <h3>User</h3>
        <pre>${JSON.stringify(log.user, null, 2)}</pre>
        
        <h3>Guild</h3>
        <pre>${JSON.stringify(log.guild, null, 2)}</pre>
        
        <h3>Channel</h3>
        <pre>${JSON.stringify(log.channel, null, 2)}</pre>
        
        <h3>Interaction</h3>
        <pre>${JSON.stringify(log.interaction, null, 2)}</pre>
        
        ${
          log.error
            ? `<h3>Error</h3><pre>${JSON.stringify(log.error, null, 2)}</pre>`
            : ""
        }
    `;
  modal.style.display = "block";
}

// Cerrar el modal
span.onclick = function () {
  modal.style.display = "none";
};

// Cerrar el modal si se hace clic fuera de él
window.onclick = function (event) {
  if (event.target == modal) {
    modal.style.display = "none";
  }
};

loadLogs();
setInterval(loadLogs, 60000); // Recargar logs cada minuto

             """),
            ('dcp_module/monitor/client/js', 'main.js', """
document.addEventListener("DOMContentLoaded", function () {
  const navItems = document.querySelectorAll(".navbar li");
  const contentFrame = document.getElementById("contentFrame");
  const darkModeToggle = document.getElementById("darkModeToggle");

  // Función para cambiar de página
  function changePage(page) {
    contentFrame.src = `pages/${page}.html`;
    navItems.forEach((item) => item.classList.remove("active"));
    document.querySelector(`[data-page="${page}"]`).classList.add("active");
  }

  // Event listeners para los items del navbar
  navItems.forEach((item) => {
    item.addEventListener("click", function () {
      changePage(this.dataset.page);
    });
  });

  // Toggle del modo oscuro
  darkModeToggle.addEventListener("click", function () {
    document.body.classList.toggle("dark-mode");
  });

  // Cargar el simulador por defecto
  changePage("simulator");
});

             """),
            ('dcp_module/monitor/client/js', 'network.js', """
document.addEventListener("DOMContentLoaded", () => {
  const requestList = document.getElementById("request-list");
  const detailsContent = document.getElementById("details-content");
  const loadingOverlay = document.getElementById("loading-overlay");
  let networkData = [];

  async function loadNetworkData() {
    try {
      loadingOverlay.style.display = "flex";
      const response = await fetch("http://127.0.0.1:8000/logs/network.json");
      networkData = await response.json();
      renderRequestList();
    } catch (error) {
      console.error("Error loading network data:", error);
    } finally {
      loadingOverlay.style.display = "none";
    }
  }

  function renderRequestList() {
    requestList.innerHTML = '<div class="header">Network Requests</div>';
    networkData.forEach((request, index) => {
      const requestItem = document.createElement("div");
      requestItem.classList.add("request-item");
      requestItem.innerHTML = `
        <div>${request.headers.general.request_method} ${request.headers.general.status_code.code}</div>
        <div style="font-size: 0.8em; color: var(--text-muted);">${request.headers.general.request_url}</div>
      `;
      requestItem.addEventListener("click", () => showRequestDetails(index));
      requestList.appendChild(requestItem);
    });
  }

  function showRequestDetails(index) {
    const request = networkData[index];
    detailsContent.innerHTML = "";

    const importantText = document.createElement("div");
    importantText.classList.add("important-text");
    importantText.textContent = `${request.headers.general.request_method} ${request.headers.general.status_code.code}`;
    detailsContent.appendChild(importantText);

    const sections = [
      {
        title: "General",
        data: {
          URL: request.headers.general.request_url,
          Method: request.headers.general.request_method,
          Status: `${request.headers.general.status_code.code} ${request.headers.general.status_code.code_message}`,
          "Remote Address": request.headers.general.remote_address,
        },
      },
      { title: "Request Headers", data: request.headers.request_headers },
      { title: "Response Headers", data: request.headers.response_headers },
      {
        title: "Response",
        data: {
          Timestamp: request.response[0].timestamp,
          Message: request.response[0].message,
        },
      },
      {
        title: "Initiator",
        data: {
          "Modified File": request.initiator.request_call_stack.modified_file,
          "Initiator Chain":
            request.initiator.request_initiator_chain.join(", "),
        },
      },
      {
        title: "Timing",
        data: {
          Total: request.timing.total,
        },
      },
    ];

    sections.forEach((section, sectionIndex) => {
      setTimeout(() => {
        const sectionElement = document.createElement("div");
        sectionElement.classList.add("detail-section");
        sectionElement.innerHTML = `<h3>${section.title}</h3>`;

        Object.entries(section.data).forEach(([key, value], itemIndex) => {
          setTimeout(() => {
            const item = document.createElement("div");
            item.classList.add("detail-item");
            item.innerHTML = `<strong>${key}:</strong> <span class="animated-text">${value}</span>`;
            sectionElement.appendChild(item);
            animateText(item.querySelector(".animated-text"));
          }, itemIndex * 100);
        });

        detailsContent.appendChild(sectionElement);
        setTimeout(() => sectionElement.classList.add("visible"), 50);
      }, sectionIndex * 200);
    });

    document.querySelectorAll(".request-item").forEach((item, i) => {
      item.classList.toggle("active", i === index);
    });
  }

  function animateText(element) {
    const text = element.textContent;
    element.textContent = "";
    [...text].forEach((char, index) => {
      const span = document.createElement("span");
      span.textContent = char;
      span.style.animationDelay = `${index * 50}ms`;
      span.classList.add("wave-animation");
      element.appendChild(span);
    });
  }

  loadNetworkData();
});

             """),
            ('dcp_module/monitor/client/js', 'performance.js', """
document.addEventListener("DOMContentLoaded", async function () {
  const ramUsageEl = document.getElementById("ram-usage");
  const flaskRamEl = document.getElementById("flask-ram");
  const prodRamEl = document.getElementById("prod-ram");
  const costEl = document.getElementById("cost");

  async function fetchPerformanceData() {
    try {
      const response = await fetch("/logs/performance.json");
      const data = await response.json();

      ramUsageEl.textContent = data.bot_memory_usage_MB.toFixed(2);
      flaskRamEl.textContent = data.flask_memory_usage_MB.toFixed(2);
      prodRamEl.textContent = data.estimated_bot_ram_production_MB.toFixed(2);

      // Estimación del costo basado en un promedio de VPS
      const estimatedCost = data.estimated_cost_per_hour; // Costo mensual
      costEl.textContent = estimatedCost.toFixed(2);
    } catch (error) {
      console.error("Error fetching performance data:", error);
    }
  }

  fetchPerformanceData();
  setInterval(fetchPerformanceData, 5000);
});

             """),
            ('dcp_module/monitor/client/js', 'security.js', """"""),
            ('dcp_module/monitor/client/js', 'simulator.js', """
// ======= SETUP DEL SIMULADOR =======
let formData = {};
let currentStep = 1;
const totalSteps = 5;

if (localStorage.getItem("formData")) {
  document.getElementById("setup").style.display = "none";
}

function showNextStep(step) {
  if (step > totalSteps) return;
  const setup = document.getElementById("setup");
  setup.style.transform = `translateX(-${(step - 1) * 100}vw)`;
  currentStep = step;
}

function saveAndNext(inputId, nextStep) {
  const inputElement = document.getElementById(inputId);
  formData[inputId] = inputElement.value;
  showNextStep(nextStep);
}

function saveAndFinish(inputId) {
  const inputElement = document.getElementById(inputId);
  formData[inputId] = inputElement.value;

  // Guardar los datos en localStorage
  localStorage.setItem("formData", JSON.stringify(formData));

  // Ocultar el formulario de configuración
  const setup = document.getElementById("setup");
  setup.style.display = "none";

  setup(formData);
}

const SERVER_URL = "http://localhost:3001";

// ======= CONECTAR AL SIMULADOR =======
function init() {
  // Intentar recuperar formData desde localStorage
  const storedData = localStorage.getItem("formData");
  if (storedData) {
    formData = JSON.parse(storedData);
  }

  // Verificar si todos los campos necesarios están presentes y no son "falsy"
  const { botToken, clientId, serverId, channelId } = formData;
  if (!botToken || !clientId || !serverId || !channelId) {
    alert(
      "❌ Debes proporcionar el Bot Token, el ID del cliente, el ID del servidor y el ID del canal."
    );
  } else {
    setupData(formData);
    fetchCommands();
    fetchMessages();
    fetchMembers();
  }
}

async function setupData(formData) {
  channelId = formData.channelId;
  guildId = formData.serverId;
  botToken = formData.botToken;
  clientId = formData.clientId;

  await fetch(`${SERVER_URL}/setup`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      botToken,
      clientId,
      guildId,
      channelId,
    }),
  });
}

// ======= OBTENER COMANDOS DEL BOT =======
function fetchCommands() {
  fetch(`${SERVER_URL}/commands`)
    .then((response) => response.json())
    .then((commands) => {
      const commandList = document.getElementById("commandList");
      commandList.innerHTML = ""; // Limpiar la lista anterior

      commands.forEach((cmd) => {
        const btn = document.createElement("button");
        btn.textContent = `/${cmd.name}`;
        btn.addEventListener("click", () => executeCommand(cmd.id, cmd.name));
        commandList.appendChild(btn);
      });
    })
    .catch((error) => console.error("❌ Error al obtener comandos:", error));
}

// ======= EJECUTAR COMANDO SLASH =======
function executeCommand(commandId, commandName) {
  fetch(`${SERVER_URL}/interactions`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      commandId,
      guildId,
      channelId,
      commandName,
    }),
  })
    .then((response) => response.json())
    .then((data) => {
      console.log("✅ Respuesta del bot:", data);
      displayMessage("Bot", data.content || "✅ Comando ejecutado.");
    })
    .catch((error) => console.error("❌ Error al ejecutar el comando:", error));
}

// ======= OBTENER MENSAJES =======
function fetchMessages() {
  fetch(`${SERVER_URL}/messages/${channelId}`)
    .then((response) => response.json())
    .then((messages) => {
      const messageArea = document.getElementById("messageArea");
      messageArea.innerHTML = "";
      messages.reverse().forEach((msg) => {
        displayMessage(msg.author.username, msg.content);
      });
    })
    .catch((error) => console.error("❌ Error al obtener mensajes:", error));
}

// ======= MOSTRAR MENSAJES EN EL CHAT =======
function displayMessage(author, content) {
  const messageArea = document.getElementById("messageArea");
  const messageElement = document.createElement("div");
  messageElement.classList.add("message");

  messageElement.innerHTML = `<strong>${author}:</strong> ${content}`;
  messageArea.appendChild(messageElement);
  messageArea.scrollTop = messageArea.scrollHeight;
}

function fetchMembers() {
  fetch(`${SERVER_URL}/members/${guildId}`)
    .then((response) => response.json())
    .then((members) => {
      console.log(members);
      const memberList = document.getElementById("memberList");
      memberList.innerHTML = "";
      members.forEach((member) => {
        const li = document.createElement("li");
        li.textContent = `${member.username}`;
        memberList.appendChild(li);
      });
    })
    .catch((error) => console.error("❌ Error al obtener miembros:", error));
}

//init();

// Agregar manejador para el botón "Send" o el input
document
  .getElementById("sendButton")
  .addEventListener("click", handleSendMessage);
document.getElementById("messageInput").addEventListener("keypress", (e) => {
  if (e.key === "Enter") {
    handleSendMessage();
  }
});

function handleSendMessage() {
  const messageInput = document.getElementById("messageInput");
  const message = messageInput.value.trim();
  if (!message) return;

  // Si el mensaje empieza con "/", es un comando
  if (message.startsWith("/")) {
    // Extraer el nombre del comando (ejemplo: "/ping" → "ping")
    const commandName = message.slice(1);

    // Opcional: aquí podrías buscar en una lista de comandos para obtener el ID correspondiente.
    // Por simplicidad, se usa commandName como id.
    executeCommand(commandName, commandName);
  } else {
    // Enviar mensaje normal (si tienes lógica para ello)
    displayMessage("Usuario", message);
  }

  messageInput.value = "";
}

             """),
            ('dcp_module/monitor/client/js', 'testing.js', """
document.addEventListener("DOMContentLoaded", () => {
  const runButtons = document.querySelectorAll(".run-btn");
  const resultOutput = document.getElementById("result-output");
  const loader = document.getElementById("loader");

  function parseStdout(stdout) {
    const lines = stdout.split("\n");

    return lines
      .map((line) => {
        const requestMatch = line.match(/^HTTP (\w+) Request to (.+)$/);
        if (requestMatch) {
          const method = requestMatch[1];
          const url = requestMatch[2];
          return `<div class="request-item" data-url="${url}">
                      <div class="request-method">${method}</div>
                      <div class="request-url">${url}</div>
                    </div>`;
        }

        const redirectMatch = line.match(/^HTTP (\d{3}) .* Redirect to (.+)$/);
        if (redirectMatch) {
          const status = redirectMatch[1];
          const url = redirectMatch[2];
          return `<div class="request-item" data-url="${url}">
                      <div class="request-method">${status} Redirect</div>
                      <div class="request-url">${url}</div>
                    </div>`;
        }

        const errorMatch = line.match(/^HTTP (\d{3}) (.+)$/);
        if (errorMatch) {
          const status = errorMatch[1];
          const message = errorMatch[2];
          return `<div class="request-item" data-url="">
                      <div class="request-method">${status} ${message}</div>
                      <div class="request-url"></div>
                    </div>`;
        }

        return line; // Si no coincide con nada, se devuelve la línea original
      })
      .join("");
  }

  runButtons.forEach((button) => {
    button.addEventListener("click", async () => {
      const testId = button.getAttribute("data-test-id");
      resultOutput.textContent = ""; // Limpiar resultado anterior
      loader.style.display = "block"; // Mostrar loader

      try {
        const response = await fetch("http://127.0.0.1:8000/run_test", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({ test_id: testId }),
        });

        const data = await response.json();
        resultOutput.textContent = parseStdout(data.stdout);
      } catch (error) {
        resultOutput.textContent = `Error al ejecutar el test: ${error.message}`;
      } finally {
        loader.style.display = "none"; // Ocultar loader
      }
    });
  });
});

             """),
            ('dcp_module/monitor/client/js', 'tutorials.js', """"""),
            
            #icons monitor
            ('dcp_module/monitor/client/media', '__init__.py', ""),
            ('dcp_module/monitor/client/media/icons', 'facebook.svg', """
<svg width="11" height="21" viewBox="0 0 11 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M7 12H9.5L10.5 8H7V6C7 4.97 7 4 9 4H10.5V0.64C10.174 0.597 8.943 0.5 7.643 0.5C4.928 0.5 3 2.157 3 5.2V8H0V12H3V20.5H7V12Z" fill="#607B96"/>
</svg>

             """),
            ('dcp_module/monitor/client/media/icons', 'github.svg', """
<svg width="21" height="20" viewBox="0 0 21 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.9771 0.278076C5.45205 0.278076 0.977052 4.75308 0.977052 10.2781C0.975918 12.3774 1.63587 14.4237 2.86327 16.1268C4.09067 17.8298 5.8232 19.1031 7.81505 19.7661C8.31505 19.8531 8.50205 19.5531 8.50205 19.2901C8.50205 19.0531 8.48905 18.2661 8.48905 17.4281C5.97705 17.8911 5.32705 16.8161 5.12705 16.2531C5.01405 15.9651 4.52705 15.0781 4.10205 14.8401C3.75205 14.6531 3.25205 14.1901 4.08905 14.1781C4.87705 14.1651 5.43905 14.9031 5.62705 15.2031C6.52705 16.7151 7.96505 16.2901 8.53905 16.0281C8.62705 15.3781 8.88905 14.9411 9.17705 14.6911C6.95205 14.4411 4.62705 13.5781 4.62705 9.75308C4.62705 8.66508 5.01405 7.76608 5.65205 7.06508C5.55205 6.81508 5.20205 5.79008 5.75205 4.41508C5.75205 4.41508 6.58905 4.15308 8.50205 5.44108C9.31611 5.21514 10.1572 5.10142 11.0021 5.10308C11.8521 5.10308 12.7021 5.21508 13.5021 5.44008C15.4141 4.14008 16.2521 4.41608 16.2521 4.41608C16.8021 5.79108 16.4521 6.81608 16.3521 7.06608C16.9891 7.76608 17.3771 8.65308 17.3771 9.75308C17.3771 13.5911 15.0401 14.4411 12.8151 14.6911C13.1771 15.0031 13.4901 15.6031 13.4901 16.5411C13.4901 17.8781 13.4771 18.9531 13.4771 19.2911C13.4771 19.5531 13.6651 19.8651 14.1651 19.7651C16.1501 19.0949 17.875 17.8191 19.097 16.1172C20.3189 14.4154 20.9765 12.3732 20.9771 10.2781C20.9771 4.75308 16.5021 0.278076 10.9771 0.278076Z" fill="#607B96"/>
</svg>

             """),
            ('dcp_module/monitor/client/media/icons', 'twitter.svg', """
<svg width="21" height="17" viewBox="0 0 21 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.383 2.15599C19.6196 2.49368 18.81 2.71546 17.981 2.81399C18.8548 2.29142 19.5087 1.469 19.821 0.499988C19.001 0.987988 18.102 1.32999 17.165 1.51499C16.5356 0.841567 15.7014 0.394951 14.792 0.244572C13.8826 0.0941939 12.9489 0.248479 12.1363 0.683441C11.3236 1.1184 10.6774 1.80967 10.2981 2.64978C9.91884 3.48989 9.82775 4.43177 10.039 5.32899C8.37611 5.24564 6.74933 4.81351 5.26429 4.06065C3.77924 3.30779 2.46913 2.25104 1.419 0.958988C1.04729 1.59744 0.851955 2.32321 0.853 3.06199C0.853 4.51199 1.591 5.79299 2.713 6.54299C2.04901 6.52208 1.39963 6.34277 0.819 6.01999V6.07199C0.8192 7.03769 1.15337 7.9736 1.76485 8.72105C2.37633 9.46849 3.22748 9.98146 4.174 10.173C3.55761 10.34 2.91131 10.3646 2.284 10.245C2.55087 11.0762 3.07101 11.8032 3.77159 12.3241C4.47218 12.845 5.31813 13.1338 6.191 13.15C5.32348 13.8313 4.33018 14.335 3.26788 14.6322C2.20558 14.9293 1.09513 15.0142 0 14.882C1.9117 16.1114 4.1371 16.7641 6.41 16.762C14.103 16.762 18.31 10.389 18.31 4.86199C18.31 4.68199 18.305 4.49999 18.297 4.32199C19.1159 3.73016 19.8226 2.99701 20.384 2.15699L20.383 2.15599Z" fill="#607B96"/>
</svg>

             """),
            
            #server monitor
            ('dcp_module/monitor/.[server]', '__init__.py', ""),
            ('dcp_module/monitor/.[server]', 'server.py', """#working"""),
            
            # Funciones del monitor
            ('dcp_module/functions', '__init__.py', ''),
            ('dcp_module/functions', 'print_logs.py', """
from datetime import datetime
import json
import os

async def logguer(interaction, file_name, msg, error_details=None):
    log_file = f'../logs/{file_name}'
    try:
        # Asegurarse de que el directorio de logs existe
        os.makedirs(os.path.dirname(log_file), exist_ok=True)

        # Leer logs existentes o inicializar una lista vacía
        logs = []
        if os.path.exists(log_file):
            try:
                with open(log_file, 'r', encoding='utf-8') as f:
                    content = f.read()
                    if content:
                        logs = json.loads(content)
            except json.JSONDecodeError:
                # Si el archivo existe pero no es un JSON válido, empezamos con una lista vacía
                logs = []

        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        # Información del usuario
        user = interaction.user
        user_info = {
            "id": user.id,
            "name": user.name,
            "discriminator": user.discriminator,
            "avatar": str(user.avatar.url) if user.avatar else None,
            "roles": [role.name for role in user.roles] if interaction.guild else []
        }

        # Información del servidor
        guild_info = None
        if interaction.guild:
            guild = interaction.guild
            guild_info = {
                "id": guild.id,
                "name": guild.name,
                "member_count": guild.member_count,
                "owner_id": guild.owner_id
            }

        # Información del canal
        channel = interaction.channel
        channel_info = {
            "id": channel.id,
            "name": channel.name if hasattr(channel, 'name') else "Direct Message",
            "type": str(channel.type)
        }

        # Información de la interacción
        interaction_info = {
            "id": interaction.id,
            "type": str(interaction.type),
            "command": interaction.command.name if interaction.command else "N/A"
        }

        # Detalles del error si aplica
        error_info = None
        if error_details:
            error_info = {
                "error_message": str(error_details.get('message')),
                "traceback": error_details.get('traceback'),
                "error_type": error_details.get('type')
            }

        # Construcción del registro
        log_entry = {
            "timestamp": timestamp,
            "message": msg,
            "user": user_info,
            "guild": guild_info,
            "channel": channel_info,
            "interaction": interaction_info,
            "error": error_info
        }

        # Añadir el nuevo log a la lista
        logs.append(log_entry)

        # Guardar todos los logs en el archivo
        with open(log_file, 'w', encoding='utf-8') as f:
            json.dump(logs, f, ensure_ascii=False, indent=4)

    except Exception as e:
        error_log = f'logs/error_log.txt'
        with open(error_log, 'a', encoding='utf-8') as f:
            f.write(f"{datetime.now()}: Error al registrar en el log: {str(e)}\n")
        print(f"Error al registrar en el log: {str(e)}")  # Para depuración




"""),
            
            # logs monitor
            ('dcp_module/logs', '__init__.py', ''),
            ('dcp_module/logs', 'components.json', """ """),
            ('dcp_module/logs', 'logs.json', """ """),
            ('dcp_module/logs', 'network.json', """ """),
            ('dcp_module/logs', 'performance.json', """ """),
            
            # modules monitor
            ('dcp_module/modules', '__init__.py', ''),
            ('dcp_module/modules', 'use_state.py', """"""),
            ('dcp_module/modules', 'use_effect.py', """"""),
            
            # test  monitor
            ('dcp_module/test', '__init__.py', ''),
            ('dcp_module/test', 'test_1.py', """
import pytest
from discord.ext import commands
import discord  # Necesitamos importar discord para los Intents


# Fixture para el bot con comando 'hello'
@pytest.fixture
def test_bot_hello():
    intents = discord.Intents.default()  # Crea los intents por defecto
    bot = commands.Bot(command_prefix="!", intents=intents)  # Pasamos los intents al bot
    
    @bot.command()
    async def hello(ctx):
        await ctx.send(f"Hello, {ctx.author.name}!")
    
    return bot


# Test para verificar que el comando 'hello' responde correctamente
@pytest.mark.asyncio
async def test_hello_command(test_bot_hello):
    # Creamos un mock de ctx para simular diferentes autores
    class MockContext:
        def __init__(self, author_name):
            self.author = type('Author', (object,), {"name": author_name})()
            self.sent_message = None

        async def send(self, message):
            self.sent_message = message
    
    # Simulamos la interacción con diferentes autores
    authors = ["User1", "User2", "User3"]
    for author_name in authors:
        ctx = MockContext(author_name)
        await test_bot_hello.get_command('hello').callback(ctx)
        
        # Verifica que el mensaje enviado sea el esperado para cada autor
        assert ctx.sent_message == f"Hello, {author_name}!"
        print(f"Test for {author_name} passed!")  # Mensaje para ver en consola

             """),
            ('dcp_module/test', 'test_2.py', """
import pytest
from discord.ext import commands
from unittest.mock import MagicMock
import discord


# Fixture para el bot con manejo de error de comando no encontrado
@pytest.fixture
def test_bot_error():
    intents = discord.Intents.default()  # Crea un objeto de intents predeterminados
    bot = commands.Bot(command_prefix="!", intents=intents)
    
    @bot.event
    async def on_command_error(ctx, error):
        if isinstance(error, commands.CommandNotFound):
            await ctx.send("Comando no encontrado!")
    
    return bot


# Test para verificar que el bot responde con "Comando no encontrado!" cuando el comando no es encontrado
@pytest.mark.asyncio
async def test_command_not_found(test_bot_error):
    # Creamos un mock del contexto (ctx) para simular el comportamiento del bot sin hacer llamadas a la API
    class MockContext:
        def __init__(self):
            self.sent_message = None

        async def send(self, message):
            self.sent_message = message
    
    # Creamos el mock de contexto
    ctx = MockContext()
    
    # Simulamos un mensaje de un autor de prueba
    mock_message = MagicMock()
    mock_message.content = "!nonexistentcommand"
    mock_message.author = MagicMock(name="author", id=12345)
    mock_message.channel = MagicMock(name="channel")

    # Procesamos el comando con el mensaje simulado
    await test_bot_error.process_commands(mock_message)

    # Simulamos el error de comando no encontrado
    error = commands.CommandNotFound("Command not found")
    await test_bot_error.on_command_error(ctx, error)

    # Verifica que el mensaje enviado sea el esperado
    assert ctx.sent_message == "Comando no encontrado!"
    print("Test for command not found passed!")  # Mensaje para ver en consola

             """),
            ('dcp_module/test', 'test_3.py', """
import pytest
from discord.ext import commands
import discord

@pytest.fixture
def bot_hello():
    intents = discord.Intents.default()
    bot = commands.Bot(command_prefix="!", intents=intents)

    @bot.command()
    async def hello(ctx):
        await ctx.send(f"Hello, {ctx.author.name}!")

    return bot

@pytest.mark.asyncio
async def test_hello_command(bot_hello):
    class MockContext:
        def __init__(self, author_name):
            self.author = type('Author', (object,), {"name": author_name})()
            self.sent_message = None

        async def send(self, message):
            self.sent_message = message

    ctx = MockContext(author_name="TestUser")
    await bot_hello.get_command('hello').callback(ctx)

    assert ctx.sent_message == "Hello, TestUser!"

             """),
            
            # runner
            ('dcp_module', 'run.py', """
import signal
import os
import re
import json
import time
import threading
import traceback
import psutil
import requests
import urllib3
import hashlib
import logging
import subprocess
import webbrowser
from datetime import datetime
from urllib.parse import urlparse
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
from rich.console import Console
from rich.progress import Progress
from flask import Flask, send_from_directory, request, jsonify


# -------------------- WATCHDOG & PROCESS MANAGEMENT --------------------
class ChangeHandler(FileSystemEventHandler):
    def __init__(self, command, root_path):
        self.command = command
        self.process = None
        self.console = Console()
        self.debounce_timer = None
        self.root_path = root_path
        self.last_hash = None

    def calculate_directory_hash(self):
        # Calcula un hash para todos los archivos `.py` en el proyecto.
        hasher = hashlib.md5()
        try:
            for subdir, _, files in os.walk(self.root_path):
                for file in files:
                    if file.endswith(".py"):
                        file_path = os.path.join(subdir, file)
                        with open(file_path, 'rb') as f:
                            buf = f.read()
                            hasher.update(buf)
            return hasher.hexdigest()
        except Exception as e:
            self.console.print(f"[bold red]Error calculating directory hash: {e}[/bold red]")
            return None

    def process_file_for_urls(self, file_path):
        try:
            with open(file_path, "r", encoding="utf-8") as f:
                content = f.read()
        except Exception as e:
            self.console.print(f"[bold red]No cant reed {file_path}: {e}[/bold red]")
            return
        # Expresión regular para encontrar URLs
        urls = re.findall(r'(https?://[^\s\'"]+)', content)
        if urls:
            self.console.print(f"[bold blue]{len(urls)} URL(s) in {file_path}[/bold blue]")
            for url in urls:
                simulate_request_and_log(url, file_path)
    
    def on_modified(self, event):
        # Solo monitorear archivos Python
        if event.is_directory or not event.src_path.endswith('.py'):
            return

        # Reiniciar el temporizador de debounce
        if self.debounce_timer:
            self.debounce_timer.cancel()

        # Corrige el uso de Timer pasando la función y sus argumentos sin llamarla inmediatamente.
        self.debounce_timer = threading.Timer(1, self.restart_if_needed, [event])
        self.debounce_timer.start()

    def restart_if_needed(self, event):
        current_hash = self.calculate_directory_hash()
        if current_hash != self.last_hash:
            self.last_hash = current_hash
            self.restart_process(event=event)

    def start_process(self):
        # Iniciar el proceso (por ejemplo, el bot)
        if not self.process or self.process.poll() is not None:
            self.console.print(f"[bold green]Running command:[/bold green] [italic yellow]{self.command}[/italic yellow]")
            with Progress() as progress:
                task = progress.add_task("[cyan]Starting process...", total=100)
                for i in range(100):
                    progress.update(task, advance=1)
                    time.sleep(0.001)  # Simula una carga
            try:
                if os.name == 'nt':  # Windows
                    self.process = subprocess.Popen(self.command, shell=True, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
                else:  # Unix (Linux/macOS)
                    self.process = subprocess.Popen(self.command, shell=True, preexec_fn=os.setsid)
                self.console.print("[bold green]Process started successfully![/bold green]")
            except FileNotFoundError as e:
                self.console.print(f"[bold red]File not found: {e}[/bold red]")
            except PermissionError as e:
                self.console.print(f"[bold red]Permission denied: {e}[/bold red]")
            except Exception as e:
                self.console.print(f"[bold red]Error starting process: {e}[/bold red]")
        else:
            self.console.print("[bold yellow]Process is already running.[/bold yellow]")

    def stop_process(self):
        if self.process:
            self.console.print("[bold red]Stopping the current process with Ctrl+C...[/bold red]")
            try:
                if os.name == 'nt':
                    self.process.send_signal(signal.CTRL_BREAK_EVENT)
                else:
                    os.killpg(os.getpgid(self.process.pid), signal.SIGINT)
                self.process.wait()
                self.console.print("[bold red]Process stopped.[/bold red]")
            except Exception as e:
                self.console.print(f"[bold red]Error stopping process: {e}[/bold red]")

    def restart_process(self, event):
        self.stop_process()
        self.start_process()
        
        commands_path = os.path.join(self.root_path, "commands")
        update_components_tree(commands_path)
                
        # Procesar archivo para encontrar URLs
        self.process_file_for_urls(event.src_path)

    def stop(self):
        if self.debounce_timer:
            self.debounce_timer.cancel()
        if self.process:
            self.console.print("[bold red]Terminating process before exit...[/bold red]")
            self.stop_process()




# -------------------- CONFIGURACIÓN DE LOGS DE NETWORK --------------------

# Definimos la ruta para los logs de red dentro de dcp_module/logs
logs_dir = os.path.join(os.path.dirname(__file__), "logs")
if not os.path.exists(logs_dir):
    os.makedirs(logs_dir)
network_log_file = os.path.join(logs_dir, "network.json")

# Inicializamos network.json con una lista vacía si no existe
if not os.path.exists(network_log_file):
    with open(network_log_file, "w", encoding="utf-8") as f:
        json.dump([], f, indent=4)


# -------------------- FUNCIÓN PARA SIMULAR LA PETICIÓN Y REGISTRAR --------------------
def simulate_request_and_log(url, modified_file):
    try:
        urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
        
        start = time.perf_counter()
        response = requests.get(url, timeout=5)
        end = time.perf_counter()
        total_time = (end - start) * 1000  # en milisegundos

        # Extraer información de la URL
        parsed_url = urlparse(url)
        remote_address = f"{parsed_url.hostname}:{parsed_url.port}" if parsed_url.port else parsed_url.hostname

        # Sección "headers"
        general_headers = {
            "request_url": url,
            "request_method": "GET",
            "status_code": {
                "code": str(response.status_code),
                "code_message": response.reason.upper() if response.reason else "OK"
            },
            "remote_address": remote_address if remote_address else "N/A",
            "referrer_policy": "strict-origin-when-cross-origin"
        }
        request_headers = dict(response.request.headers) if hasattr(response, "request") and response.request.headers else {}
        response_headers = dict(response.headers)

        # Sección "response": información de la respuesta (datos de usuario/guild no aplican aquí)
        response_entry = {
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "message": f"HTTP GET Request to {url} completed",
        }

        # Sección "initiator": se registra el archivo modificado que disparó la simulación
        initiator = {
            "request_call_stack": {"modified_file": modified_file},
            "request_initiator_chain": [modified_file]
        }

        # Sección "timing": se registra el tiempo total de la petición (otros campos quedan en "N/A")
        timing = {
            "total": f"{total_time:.2f} ms"
            
        }

        log_entry = {
            "headers": {
                "general": general_headers,
                "request_headers": request_headers,
                "response_headers": response_headers
            },
            "response": [response_entry],
            "initiator": initiator,
            "timing": timing
        }

        # Leer el contenido actual de network.json, agregar la nueva entrada y escribir de nuevo
        try:
            with open(network_log_file, "r", encoding="utf-8") as f:
                data = json.load(f)
        except Exception:
            data = []

        data.append(log_entry)

        with open(network_log_file, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=4)

    except Exception as e:
        # Se omite la URL si ocurre cualquier error (por ejemplo, timeout o URL malformada)
        Console().print(f"[bold red]Error in {url}: {e}[/bold red]")

# -------------------- FUNCIONES PARA COMPONENTS --------------------
def generate_directory_tree(path):
    tree = {"name": os.path.basename(path), "type": "directory", "children": []}
    try:
        for entry in os.scandir(path):
            if entry.is_dir():
                tree["children"].append(generate_directory_tree(entry.path))
            else:
                tree["children"].append({"name": entry.name, "type": "file"})
    except Exception as e:
        Console().print(f"[bold red]Error to generate the tree: {path}: {e}[/bold red]")
    return tree

def update_components_tree(commands_dir):
    # Define la ruta del archivo components.json (en la misma carpeta que network.json)
    logs_dir = os.path.join(os.path.dirname(__file__), "logs")
    components_file = os.path.join(logs_dir, "components.json")
    
    if not os.path.exists(commands_dir):
        Console().print(f"[bold red]'commands' no exist in {commands_dir}[/bold red]")
        return

    tree = generate_directory_tree(commands_dir)
    try:
        with open(components_file, "w", encoding="utf-8") as f:
            json.dump(tree, f, indent=4)
        Console().print(f"[bold green]components.json updated.[/bold green]")
    except Exception as e:
        Console().print(f"[bold red]Error drawing in components.json: {e}[/bold red]")


# -------------------- FUNCIÓN PARA PERFORMANCE --------------------
def update_performance_info(bot_process=None):
    logs_dir = os.path.join(os.path.dirname(__file__), "logs")
    performance_file = os.path.join(logs_dir, "performance.json")

    # Obtener información del proceso principal (Flask server)
    main_process = psutil.Process(os.getpid())
    flask_memory_usage = main_process.memory_info().rss / (1024 * 1024)  # en MB

    # Si el proceso del bot está activo, obtener su memoria
    bot_memory_usage = None
    if bot_process is not None:
        try:
            bot_proc = psutil.Process(bot_process.pid)
            bot_memory_usage = bot_proc.memory_info().rss / (1024 * 1024)  # en MB
        except Exception as e:
            bot_memory_usage = 0

    # Simular estimaciones para producción:
    # Por ejemplo, asumamos que en producción el bot podría usar 1.5 veces la memoria actual.
    estimated_bot_ram_production = (bot_memory_usage or 0) * 1.5
    # Asumamos un costo: por ejemplo, $0.10 por MB por hora.
    estimated_cost_per_hour = (estimated_bot_ram_production or 0) * 0.10

    # Uso de CPU
    cpu_usage = psutil.cpu_percent(interval=1)

    # Datos dummy para gráficos (puedes personalizarlos)
    ram_usage_history = [round((bot_memory_usage or 0) + i*0.1, 2) for i in range(10)]
    cpu_usage_history = [round(cpu_usage + i*0.5, 2) for i in range(10)]

    performance_data = {
        "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        "bot_memory_usage_MB": round(bot_memory_usage or 0, 2),
        "flask_memory_usage_MB": round(flask_memory_usage, 2),
        "estimated_bot_ram_production_MB": round(estimated_bot_ram_production or 0, 2),
        "estimated_cost_per_hour": round(estimated_cost_per_hour or 0, 2),
        "cpu_usage_percent": cpu_usage,
        "graphs": {
            "ram_usage_history": ram_usage_history,
            "cpu_usage_history": cpu_usage_history
        }
    }

    try:
        with open(performance_file, "w", encoding="utf-8") as f:
            json.dump(performance_data, f, indent=4)
    except Exception as e:
        Console().print(f"[bold red]Error writing performance.json: {e}[/bold red]")

def run_performance_monitor(event_handler, interval=10):
    while True:
        update_performance_info(bot_process=event_handler.process)
        time.sleep(interval)



# -------------------- SERVIDOR FLASK --------------------
# Se asume que este archivo se encuentra en monitor/server, y el index.html está en monitor/client
app = Flask(__name__, static_folder=os.path.join(os.path.dirname(__file__), "./monitor/client"), static_url_path="")

@app.route("/")
def index():
    return send_from_directory(app.static_folder, "index.html")

@app.route("/logs/logs.json")
def get_logs():
    return send_from_directory(os.path.join(os.path.dirname(__file__), "./logs"), "logs.json")

@app.route("/logs/network.json")
def get_network():
    return send_from_directory(os.path.join(os.path.dirname(__file__), "./logs"), "network.json")

@app.route("/logs/components.json")
def get_components():
    return send_from_directory(os.path.join(os.path.dirname(__file__), "./logs"), "components.json")

@app.route("/logs/performance.json")
def get_performance():
    return send_from_directory(os.path.join(os.path.dirname(__file__), "./logs"), "performance.json")

@app.route('/run_test', methods=['POST'])
def run_test():
    # Obtén el ID del test del cuerpo de la solicitud
    data = request.get_json()
    test_id = data.get('test_id')

    # Verifica si el test_id es válido
    if test_id is None:
        return jsonify({"error": "valid 'test_id' Required"}), 400

    # Generar el nombre del archivo de test basado en el test_id
    test_file = f"test_{test_id}.py"  # Suponiendo que los archivos de test se nombran test_1.py, test_2.py, etc.

    # Verifica si el archivo de test existe
    if not os.path.exists("./tests/" + test_file):
        return jsonify({"error": f"The file {test_file} no exist"}), 404

    try:
        # Ejecutar el test usando subprocess y pytest
        result = subprocess.run(['pytest', "./tests/" + test_file], capture_output=True, text=True)

        # Devolver el resultado del test
        return jsonify({
            "status": "Test completed",
            "stdout": result.stdout,
            "stderr": result.stderr,
            "returncode": result.returncode
        }), 200

    except Exception as e:
        return jsonify({"error": str(e)}), 500


def run_flask():
    # Silenciar el logger de Werkzeug
    log = logging.getLogger('werkzeug')
    log.setLevel(logging.ERROR)  # Solo mostrar errores críticos

    # Abrir el navegador una vez que el servidor inicie
    def open_browser():
        time.sleep(1)
        webbrowser.open("http://127.0.0.1:8000")
    threading.Thread(target=open_browser).start()

    app.run(debug=False, port=8000, use_reloader=False)






# -------------------- CONFIGURACIÓN PRINCIPAL --------------------

if __name__ == "__main__":
    root_path = "../"  # Monitorear toda la carpeta raíz
    command = "python ../bot.py"  # Comando para ejecutar el bot

    event_handler = ChangeHandler(command, root_path)
    event_handler.last_hash = event_handler.calculate_directory_hash()
    event_handler.start_process()

    observer = Observer()
    observer.schedule(event_handler, root_path, recursive=True)
    observer.start()
    
    # Iniciar el monitor de performance en un hilo separado
    performance_thread = threading.Thread(target=run_performance_monitor, args=(event_handler, 10), daemon=True)
    performance_thread.start()

    console = Console()
    console.print(f"[bold green]Monitoring for changes in: {root_path}...[/bold green]")

    # Iniciar el servidor Flask en un hilo separado
    flask_thread = threading.Thread(target=run_flask, daemon=True)
    flask_thread.start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
        event_handler.stop()
        console.print("[bold red]Monitoring stopped by user.[/bold red]")

    observer.join()

             """),
            
            # Utils
            ('utils', '__init__.py', ''),
            ('utils', 'error_message.py', """
import discord
from buttons.buttons_help import ButtonsHelp
from datetime import datetime
from dcp_module.functions.print_logs import logguer

async def error_message(e, interaction, msg):
    try: 
        embed = discord.Embed(
            title="We had a problem ⚠️",
            description=f"An error occurred while executing the 😢 command. Don't worry, it's not your fault, we are working to fix the problem as soon as possible ⚒️."
        )
        
        embed.add_field(
            name=" ",
            value="●▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬●",
            inline=False)
        
        embed.add_field(
            name="Error description: ",
            value=f"{msg}",
            inline=False)
        
        embed.add_field(
            name=" ",
            value="●▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬●",
            inline=False)
        
        embed.set_image(
                url="https://nftplazas.com/wp-content/uploads/2024/06/lumittera-gameplay.png"
            )
        
        embed.color = 0x0fffff
        await interaction.response.send_message(embed=embed, ephemeral=True, view=ButtonsHelp())
        
        # Record the error in the log file
        await logguer(interaction, "logs.json", f"Error: -> Command: {msg}")

    except Exception as e:
        print(f"Ocurrio un error al enviar el mensaje de error: {str(e)}")
        await logguer(interaction, "logs.txt", f"Error: -> Command: {msg}, Error: {str(e)}")


"""),
            

            # funciones
            ('functions', '__init__.py', ''),
            ('functions', 'file_manager.py', """
import os
import discord

# Devuelve un objeto discord.File si el archivo existe, de lo contrario, devuelve None. txt, pdf etc
def get_discord_file(filename, folder='assets'):
    # Construye la ruta completa al archivo
    file_path = os.path.join(folder, filename)

    # Verifica si el archivo existe
    if os.path.isfile(file_path):
        try:
            return discord.File(file_path, filename=filename)
        except Exception as e:
            print(f"[ERROR] Error al crear el objeto discord.File: {e}")
            return None
    else:
        print(f"[INFO] Archivo no encontrado: {file_path}")
        return None

"""),
            
            # Botones
            ('buttons', '__init__.py', ''),
            ('buttons', 'buttons_help.py', """
import discord

website_url = "https://polarisweb3.org"

class ButtonsHelp(discord.ui.View):

    def __init__(self):
        super().__init__()
        self.add_item(
            discord.ui.Button(label="Oficial Web",
                              url=website_url,
                              style=discord.ButtonStyle.url))
      
"""),
            
            # Logs
            ('logs', '__init__.py', ''),
            ('logs', 'logs.txt', ''),
            
            # Config
            ('config', '__init__.py', ''),
            ('config', 'bot_config.py', """
# Configuración del bot

TOKEN = 'YOUR_TOKEN_HERE'
"""),
            
            # Gitignore
            ('', '.gitignore', """
config
config.py    
bot_config.py
dcp_module
dcp_module/logs
"""),
            
            # pytest
            ('', 'pytest.ini', """
[pytest]
asyncio_default_fixture_loop_scope = function

"""),
            
            # requirements.txt
            ('', 'requirements.txt', """

discord.py
watchdog
asyncio
datetime
requests
threading
webbrowser
hashlib
signal
logging
flask
rich
psutil
"""),     
            
            # bot.py
            ('', 'bot.py', """

# Import required modules
import asyncio
import discord
from discord.ext import commands
from discord import app_commands
from config.bot_config import TOKEN
from utils.error_message import error_message
from commands.ping import ping_layout
from functions.print_logs import logguer

# Bot configuration
intents = discord.Intents.default()
intents.message_content = True
intents.members = True
intents.reactions = True
bot = commands.Bot(command_prefix='/', intents=intents)

# Bot startup event
@bot.event
async def on_ready():
    print("Bot is Up and Ready!")
    try:
        synced = await bot.tree.sync()
        print(f'{bot.user} has connected to Discord!')
    except Exception as e:
        print(e)
        

# Ping command
@bot.tree.command(name="ping", 
                  description="Ping the bot")
async def ping_command(interaction: discord.Interaction):
    try:
        await logguer(interaction, "logs.txt", f"Execution: -> Command: Ping")
        
        # after registration, send the response
        await ping_layout(interaction, round(bot.latency * 1000))
    except Exception as e:
        await error_message(e,
                            interaction,
                            f"Command ping [{e}]")

bot.run(TOKEN)

"""),
        ]
        
        # Crear directorios
        create_project_structure(project_directory_2, directories, files)

  except Exception as e:
    console.print(e)
    print(e)