pragma Singleton

import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Services.Theming

// Service to check if various programs are available on the system
Singleton {
  id: root

  // Program availability properties
  property bool matugenAvailable: false
  property bool pywalfoxAvailable: false
  property bool alacrittyAvailable: false
  property bool kittyAvailable: false
  property bool ghosttyAvailable: false
  property bool footAvailable: false
  property bool weztermAvailable: false
  property bool fuzzelAvailable: false
  property bool vicinaeAvailable: false
  property bool walkerAvailable: false
  property bool gpuScreenRecorderAvailable: false
  property bool wlsunsetAvailable: false
  property bool app2unitAvailable: false
  property bool codeAvailable: false
  property bool gnomeCalendarAvailable: false
  property bool spicetifyAvailable: false
  property bool telegramAvailable: false
  property bool cavaAvailable: false
  property bool emacsAvailable: false
  property bool niriAvailable: false

  // Discord client auto-detection
  property var availableDiscordClients: []

  // Code client auto-detection
  property var availableCodeClients: []

  // Signal emitted when all checks are complete
  signal checksCompleted

  // Function to detect Discord client by checking config directories
  function detectDiscordClient() {
    // Build shell script to check each client
    var scriptParts = ["available_clients=\"\";"];

    for (var i = 0; i < TemplateRegistry.discordClients.length; i++) {
      var client = TemplateRegistry.discordClients[i];
      var clientName = client.name;
      var configPath = client.configPath;

      // Use the actual config path from the client, removing ~ prefix
      var checkPath = configPath.startsWith("~") ? configPath.substring(2) : configPath.substring(1);

      // Check if this client requires themes folder to exist
      if (client.requiresThemesFolder) {
        scriptParts.push("if [ -d \"$HOME/" + checkPath + "/themes\" ]; then available_clients=\"$available_clients " + clientName + "\"; fi;");
      } else {
        scriptParts.push("if [ -d \"$HOME/" + checkPath + "\" ]; then available_clients=\"$available_clients " + clientName + "\"; fi;");
      }
    }

    scriptParts.push("echo \"$available_clients\"");

    // Use a Process to check directory existence for all clients
    discordDetector.command = ["sh", "-c", scriptParts.join(" ")];
    discordDetector.running = true;
  }

  // Process to detect Discord client directories
  Process {
    id: discordDetector
    running: false

    onExited: function (exitCode) {
      availableDiscordClients = [];

      if (exitCode === 0) {
        var detectedClients = stdout.text.trim().split(/\s+/).filter(function (client) {
          return client.length > 0;
        });

        if (detectedClients.length > 0) {
          // Build list of available clients
          for (var i = 0; i < detectedClients.length; i++) {
            var clientName = detectedClients[i];
            for (var j = 0; j < TemplateRegistry.discordClients.length; j++) {
              var client = TemplateRegistry.discordClients[j];
              if (client.name === clientName) {
                availableDiscordClients.push(client);
                break;
              }
            }
          }

          Logger.d("ProgramChecker", "Detected Discord clients:", detectedClients.join(", "));
        }
      }

      if (availableDiscordClients.length === 0) {
        Logger.d("ProgramChecker", "No Discord clients detected");
      }
    }

    stdout: StdioCollector {}
    stderr: StdioCollector {}
  }

  // Function to detect Code client by checking config directories
  function detectCodeClient() {
    // Build shell script to check each client
    var scriptParts = ["available_clients=\"\";"];

    for (var i = 0; i < TemplateRegistry.codeClients.length; i++) {
      var client = TemplateRegistry.codeClients[i];
      var clientName = client.name;
      var configPath = client.configPath;

      // Check if the config directory exists
      scriptParts.push("if [ -d \"$HOME" + configPath.substring(1) + "\" ]; then available_clients=\"$available_clients " + clientName + "\"; fi;");
    }

    scriptParts.push("echo \"$available_clients\"");

    // Use a Process to check directory existence for all clients
    codeDetector.command = ["sh", "-c", scriptParts.join(" ")];
    codeDetector.running = true;
  }

  // Process to detect Code client directories
  Process {
    id: codeDetector
    running: false

    onExited: function (exitCode) {
      availableCodeClients = [];

      if (exitCode === 0) {
        var detectedClients = stdout.text.trim().split(/\s+/).filter(function (client) {
          return client.length > 0;
        });

        if (detectedClients.length > 0) {
          // Build list of available clients
          for (var i = 0; i < detectedClients.length; i++) {
            var clientName = detectedClients[i];
            for (var j = 0; j < TemplateRegistry.codeClients.length; j++) {
              var client = TemplateRegistry.codeClients[j];
              if (client.name === clientName) {
                availableCodeClients.push(client);
                break;
              }
            }
          }

          Logger.d("ProgramChecker", "Detected Code clients:", detectedClients.join(", "));
        }
      }

      if (availableCodeClients.length === 0) {
        Logger.d("ProgramChecker", "No Code clients detected");
      }
    }

    stdout: StdioCollector {}
    stderr: StdioCollector {}
  }

  // Programs to check - maps property names to commands
  readonly property var programsToCheck: ({
                                            "matugenAvailable": ["which", "matugen"],
                                            "pywalfoxAvailable": ["which", "pywalfox"],
                                            "alacrittyAvailable": ["which", "alacritty"],
                                            "kittyAvailable": ["which", "kitty"],
                                            "ghosttyAvailable": ["which", "ghostty"],
                                            "footAvailable": ["which", "foot"],
                                            "weztermAvailable": ["which", "wezterm"],
                                            "fuzzelAvailable": ["which", "fuzzel"],
                                            "vicinaeAvailable": ["sh", "-c", "command -v vicinae >/dev/null 2>&1 || (IFS=:; find $PATH -maxdepth 1 -iname 'vicinae*.appimage' -type f -executable 2>/dev/null | grep -q .)"],
                                            "walkerAvailable": ["which", "walker"],
                                            "app2unitAvailable": ["which", "app2unit"],
                                            "gpuScreenRecorderAvailable": ["sh", "-c", "command -v gpu-screen-recorder >/dev/null 2>&1 || (command -v flatpak >/dev/null 2>&1 && flatpak list --app | grep -q 'com.dec05eba.gpu_screen_recorder')"],
                                            "wlsunsetAvailable": ["which", "wlsunset"],
                                            "codeAvailable": ["which", "code"],
                                            "gnomeCalendarAvailable": ["which", "gnome-calendar"],
                                            "spicetifyAvailable": ["which", "spicetify"],
                                            "telegramAvailable": ["sh", "-c", "command -v telegram-desktop >/dev/null 2>&1 || command -v Telegram >/dev/null 2>&1 || (command -v flatpak >/dev/null 2>&1 && flatpak list --app | grep -q 'org.telegram.desktop')"],
                                            "cavaAvailable": ["which", "cava"],
                                            "emacsAvailable": ["sh", "-c", "test -d \"$HOME/.config/doom\" || test -d \"$HOME/.emacs.d\""],
                                            "niriAvailable": ["which", "niri"]
                                          })

  // Internal tracking
  property int completedChecks: 0
  property int totalChecks: Object.keys(programsToCheck).length

  // Single reusable Process object
  Process {
    id: checker
    running: false

    property string currentProperty: ""

    onExited: function (exitCode) {
      // Set the availability property
      root[currentProperty] = (exitCode === 0);

      // Stop the process to free resources
      running = false;

      // Track completion
      root.completedChecks++;

      // Check next program or emit completion signal
      if (root.completedChecks >= root.totalChecks) {
        // Run Discord and Code client detection after all checks are complete
        root.detectDiscordClient();
        root.detectCodeClient();
        root.checksCompleted();
      } else {
        root.checkNextProgram();
      }
    }

    stdout: StdioCollector {}
    stderr: StdioCollector {}
  }

  // Queue of programs to check
  property var checkQueue: []
  property int currentCheckIndex: 0

  // Function to check the next program in the queue
  function checkNextProgram() {
    if (currentCheckIndex >= checkQueue.length)
      return;
    var propertyName = checkQueue[currentCheckIndex];
    var command = programsToCheck[propertyName];

    checker.currentProperty = propertyName;
    checker.command = command;
    checker.running = true;

    currentCheckIndex++;
  }

  // Function to run all program checks
  function checkAllPrograms() {
    // Reset state
    completedChecks = 0;
    currentCheckIndex = 0;
    checkQueue = Object.keys(programsToCheck);

    // Start first check
    if (checkQueue.length > 0) {
      checkNextProgram();
    }
  }

  // Function to check a specific program
  function checkProgram(programProperty) {
    if (!programsToCheck.hasOwnProperty(programProperty)) {
      Logger.w("ProgramChecker", "Unknown program property:", programProperty);
      return;
    }

    checker.currentProperty = programProperty;
    checker.command = programsToCheck[programProperty];
    checker.running = true;
  }

  // Manual function to test Discord detection (for debugging)
  function testDiscordDetection() {
    Logger.d("ProgramChecker", "Testing Discord detection...");
    Logger.d("ProgramChecker", "HOME:", Quickshell.env("HOME"));

    // Test each client directory
    for (var i = 0; i < TemplateRegistry.discordClients.length; i++) {
      var client = TemplateRegistry.discordClients[i];
      var configDir = client.configPath.replace("~", Quickshell.env("HOME"));
      Logger.d("ProgramChecker", "Checking:", configDir);
    }

    detectDiscordClient();
  }

  // Initialize checks when service is created
  Component.onCompleted: {
    checkAllPrograms();
  }
}
