pragma Singleton

import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Services.System
import qs.Services.UI

Singleton {
  id: root

  readonly property string dynamicConfigPath: Settings.cacheDir + "matugen.dynamic.toml"

  readonly property var schemeNameMap: ({
                                          "Noctalia (default)": "Noctalia-default",
                                          "Noctalia (legacy)": "Noctalia-legacy",
                                          "Tokyo Night": "Tokyo-Night"
                                        })

  readonly property var terminalPaths: ({
                                          "foot": "~/.config/foot/themes/noctalia",
                                          "ghostty": "~/.config/ghostty/themes/noctalia",
                                          "kitty": "~/.config/kitty/themes/noctalia.conf",
                                          "alacritty": "~/.config/alacritty/themes/noctalia.toml",
                                          "wezterm": "~/.config/wezterm/colors/Noctalia.toml"
                                        })


  /**
   * Process wallpaper colors using matugen CLI
   * Dual-path architecture (wallpaper uses matugen CLI)
   */
  function processWallpaperColors(wallpaperPath, mode) {
    const content = buildMatugenConfig()
    if (!content)
      return

    const wp = wallpaperPath.replace(/'/g, "'\\''")
    const script = buildMatugenScript(content, wp, mode)

    generateProcess.generator = "matugen"
    generateProcess.command = ["bash", "-lc", script]
    generateProcess.running = true
  }


  /**
   * Process predefined color scheme using sed scripts
   * Dual-path architecture (predefined uses sed scripts)
   */
  function processPredefinedScheme(schemeData, mode) {
    handleTerminalThemes(mode)

    const colors = schemeData[mode]
    let script = processAllTemplates(colors, mode)

    // Add user templates if enabled (requirement #1)
    script += buildUserTemplateCommandForPredefined(schemeData, mode)

    generateProcess.generator = "predefined"
    generateProcess.command = ["bash", "-lc", script]
    generateProcess.running = true
  }

  // ================================================================================
  // WALLPAPER-BASED GENERATION (matugen CLI)
  // ================================================================================
  function buildMatugenConfig() {
    var lines = []
    var mode = Settings.data.colorSchemes.darkMode ? "dark" : "light"

    if (Settings.data.colorSchemes.useWallpaperColors) {
      addWallpaperTemplates(lines, mode)
    }

    addApplicationTemplates(lines, mode)

    if (lines.length > 0) {
      return ["[config]"].concat(lines).join("\n") + "\n"
    }
    return ""
  }

  function addWallpaperTemplates(lines, mode) {
    // Noctalia colors JSON
    lines.push("[templates.noctalia]")
    lines.push('input_path = "' + Quickshell.shellDir + '/Assets/MatugenTemplates/noctalia.json"')
    lines.push('output_path = "' + Settings.configDir + 'colors.json"')

    // Terminal templates
    TemplateRegistry.terminals.forEach(terminal => {
                                         if (Settings.data.templates[terminal.id]) {
                                           lines.push(`\n[templates.${terminal.id}]`)
                                           lines.push(`input_path = "${Quickshell.shellDir}/Assets/MatugenTemplates/${terminal.matugenPath}"`)
                                           lines.push(`output_path = "${terminal.outputPath}"`)
                                           const postHook = terminal.postHook || `${TemplateRegistry.colorsApplyScript} ${terminal.id}`
                                           lines.push(`post_hook = "${postHook}"`)
                                         }
                                       })
  }

  function addApplicationTemplates(lines, mode) {
    TemplateRegistry.applications.forEach(app => {
                                            if (app.id === "discord") {
                                              // Handle Discord clients specially
                                              if (Settings.data.templates.discord) {
                                                app.clients.forEach(client => {
                                                                      // Check if this specific client is detected
                                                                      if (isDiscordClientEnabled(client.name)) {
                                                                        lines.push(`\n[templates.discord_${client.name}]`)
                                                                        lines.push(`input_path = "${Quickshell.shellDir}/Assets/MatugenTemplates/${app.input}"`)
                                                                        lines.push(`output_path = "${client.path}/themes/noctalia.theme.css"`)
                                                                      }
                                                                    })
                                              }
                                            } else {
                                              // Handle regular apps
                                              if (Settings.data.templates[app.id]) {
                                                app.outputs.forEach((output, idx) => {
                                                                      lines.push(`\n[templates.${app.id}_${idx}]`)
                                                                      lines.push(`input_path = "${Quickshell.shellDir}/Assets/MatugenTemplates/${app.input}"`)
                                                                      lines.push(`output_path = "${output.path}"`)
                                                                      if (app.postProcess) {
                                                                        lines.push(`post_hook = "${app.postProcess(mode)}"`)
                                                                      }
                                                                    })
                                              }
                                            }
                                          })
  }

  function isDiscordClientEnabled(clientName) {
    // Check ProgramCheckerService to see if client is detected
    for (var i = 0; i < ProgramCheckerService.availableDiscordClients.length; i++) {
      if (ProgramCheckerService.availableDiscordClients[i].name === clientName) {
        return true
      }
    }
    return false
  }

  function buildMatugenScript(content, wallpaper, mode) {
    const delimiter = "MATUGEN_CONFIG_EOF_" + Math.random().toString(36).substr(2, 9)
    const pathEsc = dynamicConfigPath.replace(/'/g, "'\\''")

    let script = `cat > '${pathEsc}' << '${delimiter}'\n${content}\n${delimiter}\n`
    script += `matugen image '${wallpaper}' --config '${pathEsc}' --mode ${mode} --type ${Settings.data.colorSchemes.matugenSchemeType}`
    script += buildUserTemplateCommand(wallpaper, mode)

    return script + "\n"
  }

  // ================================================================================
  // PREDEFINED SCHEME GENERATION (sed scripts)
  // ================================================================================
  function processAllTemplates(colors, mode) {
    let script = ""
    const homeDir = Quickshell.env("HOME")

    TemplateRegistry.applications.forEach(app => {
                                            if (app.id === "discord") {
                                              if (Settings.data.templates.discord) {
                                                script += processDiscordClients(app, colors, mode, homeDir)
                                              }
                                            } else {
                                              if (Settings.data.templates[app.id]) {
                                                script += processTemplate(app, colors, mode, homeDir)
                                              }
                                            }
                                          })
    return script
  }

  function processDiscordClients(discordApp, colors, mode, homeDir) {
    let script = ""
    const palette = ColorPaletteGenerator.generatePalette(colors, Settings.data.colorSchemes.darkMode, false)

    discordApp.clients.forEach(client => {
                                 if (!isDiscordClientEnabled(client.name))
                                 return

                                 const templatePath = `${Quickshell.shellDir}/Assets/MatugenTemplates/${discordApp.input}`
                                 const outputPath = `${client.path}/themes/noctalia.theme.css`.replace("~", homeDir)
                                 const outputDir = outputPath.substring(0, outputPath.lastIndexOf('/'))
                                 const baseConfigDir = outputDir.replace("/themes", "")

                                 script += `\n`
                                 script += `if [ -d "${baseConfigDir}" ]; then\n`
                                 script += `  mkdir -p ${outputDir}\n`
                                 script += `  cp '${templatePath}' '${outputPath}'\n`
                                 script += `  ${replaceColorsInFile(outputPath, palette)}`
                                 script += `else\n`
                                 script += `  echo "Discord client ${client.name} not found at ${baseConfigDir}, skipping"\n`
                                 script += `fi\n`
                               })

    return script
  }

  function processTemplate(app, colors, mode, homeDir) {
    const palette = ColorPaletteGenerator.generatePalette(colors, Settings.data.colorSchemes.darkMode, app.strict || false)
    let script = ""

    app.outputs.forEach(output => {
                          const templatePath = `${Quickshell.shellDir}/Assets/MatugenTemplates/${app.input}`
                          const outputPath = output.path.replace("~", homeDir)
                          const outputDir = outputPath.substring(0, outputPath.lastIndexOf('/'))

                          script += `\n`
                          script += `mkdir -p ${outputDir}\n`
                          script += `cp '${templatePath}' '${outputPath}'\n`
                          script += replaceColorsInFile(outputPath, palette)
                          script += `\n`
                        })

    if (app.postProcess) {
      script += app.postProcess(mode)
    }

    return script
  }

  function replaceColorsInFile(filePath, colors) {
    let script = ""
    Object.keys(colors).forEach(colorKey => {
                                  const hexValue = colors[colorKey].default.hex
                                  const hexStrippedValue = colors[colorKey].default.hex_stripped

                                  const escapedHex = hexValue.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
                                  const escapedHexStripped = hexStrippedValue.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')

                                  // replace hex_stripped
                                  script += `sed -i 's/{{colors\\.${colorKey}\\.default\\.hex_stripped}}/${escapedHexStripped}/g' '${filePath}'\n`

                                  // replace hex
                                  script += `sed -i 's/{{colors\\.${colorKey}\\.default\\.hex}}/${escapedHex}/g' '${filePath}'\n`
                                })
    return script
  }

  // ================================================================================
  // TERMINAL THEMES (predefined schemes use pre-rendered files)
  // ================================================================================
  function handleTerminalThemes(mode) {
    const commands = []
    const homeDir = Quickshell.env("HOME")

    Object.keys(terminalPaths).forEach(terminal => {
                                         if (Settings.data.templates[terminal]) {
                                           const outputPath = terminalPaths[terminal].replace("~", homeDir)
                                           const outputDir = outputPath.substring(0, outputPath.lastIndexOf('/'))
                                           const templatePath = getTerminalColorsTemplate(terminal, mode)

                                           commands.push(`mkdir -p ${outputDir}`)
                                           commands.push(`cp -f ${templatePath} ${outputPath}`)
                                           commands.push(`${TemplateRegistry.colorsApplyScript} ${terminal}`)
                                         }
                                       })

    if (commands.length > 0) {
      copyProcess.command = ["bash", "-lc", commands.join('; ')]
      copyProcess.running = true
    }
  }

  function getTerminalColorsTemplate(terminal, mode) {
    let colorScheme = Settings.data.colorSchemes.predefinedScheme
    colorScheme = schemeNameMap[colorScheme] || colorScheme

    let extension = ""
    if (terminal === 'kitty') {
      extension = ".conf"
    } else if (terminal === 'wezterm') {
      extension = ".toml"
    }

    return `${Quickshell.shellDir}/Assets/ColorScheme/${colorScheme}/terminal/${terminal}/${colorScheme}-${mode}${extension}`
  }

  // ================================================================================
  // USER TEMPLATES, advanced usage
  // ================================================================================
  function buildUserTemplateCommand(input, mode) {
    if (!Settings.data.templates.enableUserTemplates)
      return ""

    const userConfigPath = getUserConfigPath()
    let script = "\n# Execute user config if it exists\n"
    script += `if [ -f '${userConfigPath}' ]; then\n`
    script += `  matugen image '${input}' --config '${userConfigPath}' --mode ${mode} --type ${Settings.data.colorSchemes.matugenSchemeType}\n`
    script += "fi"

    return script
  }

  function buildUserTemplateCommandForPredefined(schemeData, mode) {
    if (!Settings.data.templates.enableUserTemplates)
      return ""

    const userConfigPath = getUserConfigPath()
    const colors = schemeData[mode]
    const palette = ColorPaletteGenerator.generatePalette(colors, Settings.data.colorSchemes.darkMode, false)

    const tempJsonPath = Settings.cacheDir + "predefined-colors.json"
    const tempJsonPathEsc = tempJsonPath.replace(/'/g, "'\\''")

    let script = "\n# Execute user templates with predefined scheme colors\n"
    script += `if [ -f '${userConfigPath}' ]; then\n`
    script += `  cat > '${tempJsonPathEsc}' << 'EOF'\n`
    script += JSON.stringify({
                               "colors": palette
                             }, null, 2) + "\n"
    script += "EOF\n"
    script += `  matugen json '${tempJsonPathEsc}' --config '${userConfigPath}' --mode ${mode}\n`
    script += "fi"

    return script
  }

  function getUserConfigPath() {
    return (Settings.configDir + "user-templates.toml").replace(/'/g, "'\\''")
  }

  // ================================================================================
  // PROCESSES
  // ================================================================================
  Process {
    id: generateProcess
    workingDirectory: Quickshell.shellDir
    running: false

    // Error reporting helpers
    property string generator: ""

    function buildErrorMessage() {
      const description = (stderr.text && stderr.text.trim() !== "") ? stderr.text.trim() : ((stdout.text && stdout.text.trim() !== "") ? stdout.text.trim() : I18n.tr("toast.theming-processor-failed.desc-generic"))
      const title = I18n.tr(`toast.theming-processor-failed.title-${generator}`)
      return description
    }

    onExited: function (exitCode) {
      if (exitCode !== 0) {
        const description = generateProcess.buildErrorMessage()
        Logger.e("TemplateProcessor", "Process failed with exit code", exitCode, description)
      }
    }

    stdout: StdioCollector {
      onStreamFinished: {
        if (this.text)
        Logger.d("TemplateProcessor", "stdout:", this.text)
      }
    }

    stderr: StdioCollector {
      onStreamFinished: {
        if (this.text) {
          const description = generateProcess.buildErrorMessage()
          Logger.e("TemplateProcessor", "Process failed", description)
        }
      }
    }
  }

  // ------------
  Process {
    id: copyProcess
    workingDirectory: Quickshell.shellDir
    running: false
    stderr: StdioCollector {
      onStreamFinished: {
        if (this.text) {
          Logger.e("TemplateProcessor", "copyProcess stderr:", this.text)
        }
      }
    }
  }
}
