#!/usr/bin/env node

const { spawn } = require("child_process");
const path = require("path");
const fs = require("fs");

// Colors for console output
const colors = {
  reset: "\x1b[0m",
  bright: "\x1b[1m",
  green: "\x1b[32m",
  red: "\x1b[31m",
  yellow: "\x1b[33m",
  blue: "\x1b[34m",
  magenta: "\x1b[35m",
  cyan: "\x1b[36m",
};

const log = {
  info: (msg) => console.log(`${colors.blue}ℹ${colors.reset} ${msg}`),
  success: (msg) => console.log(`${colors.green}✅${colors.reset} ${msg}`),
  error: (msg) => console.log(`${colors.red}❌${colors.reset} ${msg}`),
  warning: (msg) => console.log(`${colors.yellow}⚠️${colors.reset} ${msg}`),
  server: (msg) => console.log(`${colors.cyan}🌐${colors.reset} ${msg}`),
  watch: (msg) => console.log(`${colors.magenta}👀${colors.reset} ${msg}`),
};

class AutoDev {
  constructor() {
    this.processes = new Map();
    this.isShuttingDown = false;
  }

  async checkDependencies() {
    log.info("Checking dependencies...");

    const requiredPackages = ["nodemon", "concurrently", "express", "cors"];
    const packageJson = JSON.parse(fs.readFileSync("package.json", "utf8"));

    const missing = requiredPackages.filter(
      (pkg) =>
        !packageJson.dependencies?.[pkg] && !packageJson.devDependencies?.[pkg]
    );

    if (missing.length > 0) {
      log.error(`Missing packages: ${missing.join(", ")}`);
      log.info("Installing missing packages...");
      await this.runCommand("npm", ["install", ...missing, "--save-dev"]);
    }

    log.success("All dependencies are installed");
  }

  async runCommand(command, args, options = {}) {
    return new Promise((resolve, reject) => {
      const child = spawn(command, args, {
        stdio: "inherit",
        shell: true,
        ...options,
      });

      child.on("close", (code) => {
        if (code === 0) {
          resolve();
        } else {
          reject(new Error(`Command failed with code ${code}`));
        }
      });

      child.on("error", reject);
    });
  }
  async initialBuild() {
    log.info("Running initial build...");
    try {
      await this.runCommand("node", ["build-simple.js"]);
      log.success("Initial build completed");
    } catch (error) {
      log.error("Initial build failed:", error.message);
      throw error;
    }
  }

  startWatcher() {
    log.watch("Starting file watcher...");

    const watcher = spawn("npx", ["nodemon"], {
      stdio: ["inherit", "pipe", "pipe"],
      shell: true,
      env: { ...process.env, NODE_ENV: "development" },
    });

    watcher.stdout.on("data", (data) => {
      const output = data.toString().trim();
      if (output) {
        console.log(`${colors.magenta}[WATCH]${colors.reset} ${output}`);
      }
    });

    watcher.stderr.on("data", (data) => {
      const output = data.toString().trim();
      if (output) {
        console.log(`${colors.yellow}[WATCH]${colors.reset} ${output}`);
      }
    });

    watcher.on("close", (code) => {
      if (!this.isShuttingDown) {
        log.error(`Watcher exited with code ${code}`);
      }
    });

    this.processes.set("watcher", watcher);
    return watcher;
  }

  startDevServer() {
    log.server("Starting development server...");

    const server = spawn("node", ["dev-server.js"], {
      stdio: ["inherit", "pipe", "pipe"],
      shell: true,
    });

    server.stdout.on("data", (data) => {
      const output = data.toString().trim();
      if (output) {
        console.log(`${colors.cyan}[SERVER]${colors.reset} ${output}`);
      }
    });

    server.stderr.on("data", (data) => {
      const output = data.toString().trim();
      if (output) {
        console.log(`${colors.red}[SERVER]${colors.reset} ${output}`);
      }
    });

    server.on("close", (code) => {
      if (!this.isShuttingDown) {
        log.error(`Server exited with code ${code}`);
      }
    });

    this.processes.set("server", server);
    return server;
  }

  setupSignalHandlers() {
    const cleanup = () => {
      if (this.isShuttingDown) return;
      this.isShuttingDown = true;

      console.log("\n");
      log.info("Shutting down auto-dev environment...");

      for (const [name, process] of this.processes) {
        log.info(`Stopping ${name}...`);
        process.kill("SIGTERM");
      }

      setTimeout(() => {
        log.success("Auto-dev environment stopped");
        process.exit(0);
      }, 1000);
    };

    process.on("SIGINT", cleanup);
    process.on("SIGTERM", cleanup);
  }

  async start() {
    console.log(`
${colors.bright}🚀 Vega Providers Auto-Development Environment${colors.reset}


${colors.yellow}Press Ctrl+C to stop${colors.reset}
`);

    try {
      // Setup signal handlers
      this.setupSignalHandlers();

      // Check dependencies
      await this.checkDependencies();

      // Initial build
      await this.initialBuild();

      // Start watcher and server
      this.startWatcher();

      // Wait a bit before starting server
      setTimeout(() => {
        this.startDevServer();
      }, 2000);

      log.success("Auto-development environment is running!");
      log.info(
        "Make changes to your providers and watch them rebuild automatically"
      );
    } catch (error) {
      log.error("Failed to start auto-dev environment:", error.message);
      process.exit(1);
    }
  }
}

// CLI interface
if (require.main === module) {
  const autoDev = new AutoDev();
  autoDev.start();
}

module.exports = AutoDev;
