lib/svelte_on_rails/installer/utils.rb



module SvelteOnRails

  module Installer

    module Utils
      def self.install_npm_package
        package_name = "@csedl/svelte-on-rails@latest"
        puts "Installing #{package_name} via npm..."

        if system("npm install #{package_name}")
          puts "#{package_name} successfully installed."
        else
          abort "Failed to install #{package_name}. Please ensure npm is installed and try running 'npm install #{package_name}' manually."
        end
      end

      def self.install_turbo

        pkg_js = Rails.root.join("package.json")
        package_name = "@hotwired/turbo-rails"
        file_content = File.exist?(pkg_js) ? File.read(pkg_js) : ""

        if file_content.match?(/#{package_name}/)
          puts "#{package_name} is already present in package.json, assuming that it is set up well and working."
        else
          puts "Installing #{package_name} via npm..."
          if system("npm install #{package_name}")
            puts "#{package_name} successfully installed."
            add_line_to_file(Rails.root.join("app", "frontend", "entrypoints", "application.js"), "import '#{package_name}';")
          else
            abort "Failed to install #{package_name}. Please ensure npm is installed and try running 'npm install #{package_name}' manually."
          end
        end

      end

      def self.create_folder(folder)
        if Dir.exist?(folder)
          puts "Folder already exists: #{folder}"
        else
          FileUtils.mkdir_p(folder)
          puts "Created folder: #{folder}"
        end
      end

      def self.add_line_to_file(file_path, line)
        file_content = File.exist?(file_path) ? File.read(file_path) : ""

        if file_content.match?(/#{line}/)
          puts "#{line} already present in #{file_path}, nothing changed here."
          return
        end

        File.open(file_path, 'a') do |file|
          file.puts(line)
        end
        puts "added #{line} to #{file_path}."
      rescue StandardError => e
        puts "Error: #{e.message}"
      end

      def self.create_file(file_path)

        unless File.exist?(file_path)
          FileUtils.touch(file_path)
          puts "Created empty file at file://#{file_path}"
        end
      end

      def self.create_javascript_initializer
        config_path = Rails.root.join("app", "frontend", "initializers", "svelte.js")
        if File.exist?(config_path)
          puts "Initializer already exists: file://#{config_path}"
        else
          File.write(config_path, <<~JAVASCRIPT)

            import { initializeSvelteComponents, cleanupSvelteComponents } from '@csedl/svelte-on-rails';

            const components = import.meta.glob('/javascript/components/**/*.svelte', { eager: true });
            const componentsRoot = '/javascript/components';

            // Initialize Svelte components
            initializeSvelteComponents(componentsRoot, components, true);

            // Turbo event listener for page load
            document.addEventListener('turbo:load', () => {
                initializeSvelteComponents(componentsRoot, components, true);
            });

            // Turbo event listener for cleanup before page cache
            document.addEventListener('turbo:before-cache', () => {
                cleanupSvelteComponents(false);
            });
          JAVASCRIPT
          puts "Created initializer file at file://#{config_path}"
        end
      end

      def self.create_svelte_hello_world
        file_path = Rails.root.join("app", "frontend", "javascript", "components", "HelloWorld.svelte")
        if File.exist?(file_path)
          puts "Hello World file already exists: file://#{file_path}"
        else
          File.write(file_path, <<~HTML)
            <script>
                export let items
                let count = 0;

                function increment() {
                    count += 1;
                }
            </script>

            <h1>Greetings from svelte</h1>

            <button on:click={increment}>Increment: {count}</button>
            <ul>
                {#each items as item}
                    <li>{item}</li>
                {/each}
            </ul>

            <style>
                button {
                    background-color: darkred;
                    color: white;
                    padding: 10px;
                    border: none;
                }
            </style>
          HTML
          puts "Hello World file at file://#{file_path}"
        end
      end

      def self.run_command(command)

        Dir.chdir(Rails.root) do
          stdout, stderr, status = Open3.capture3(command)
          if stderr.present?
            puts "Error running command «#{command}»:"
            raise stderr
          else
            puts "#{command} => Success"
          end
        end

      end

      def self.check_file_exists(file_path)
        unless File.exist?(file_path)
          raise "ERROR: File not found: #{file_path}"
        end
      end

      def self.check_folder_exists(folder_path)
        unless File.exist?(folder_path)
          raise "ERROR: Folder not found: #{folder_path}"
        end
      end

      def self.check_file_not_exists(file_path)
        if File.exist?(file_path)
          raise "ERROR: File already exists: #{file_path}"
        end
      end

      def self.check_folder_not_exists(folder_path)
        if File.exist?(folder_path)
          raise "ERROR: Folder already exists: #{folder_path}"
        end
      end

      def self.write_templates(template_paths)

        existing = template_paths.select { |p| File.exist?(p) }
        verb = 'Created'

        if existing.present?
          begin
            puts "#{'Template'.pluralize(existing.length)} already exists:\n#{existing.join("\n")}.\nOverwrite? (y/n)"
            continue = STDIN.gets.chomp.downcase[0]
          end until ['y', 'n'].include?(continue)
          if continue == 'n'
            puts "Skipping write #{'template'.pluralize(template_paths.length)}."
            return
          end
          verb = 'Overwrote'
        end

        template_paths.each do |p|
          source_path = File.expand_path('rails_template', __dir__) + '/' + p
          FileUtils.mkdir_p(File.dirname(p))
          FileUtils.cp(source_path, p)
        end

        puts "#{verb} #{'file'.pluralize(template_paths.length)}:\n#{template_paths.join("\n")}"
      end

      def self.ask_yn(question)
        begin
          puts "#{question} (y/n)"
          continue = STDIN.gets.chomp.downcase[0]
        end until ['y', 'n'].include?(continue)
        continue == 'y'
      end

      def self.which_root_route

        # Check if the root route is active (uncommented) or commented out

        routes = File.read(Rails.root.join('config', 'routes.rb'))
        m = routes.match(/^\s*root\s+['"]([^'"]+)['"]/m)
        if m
          m.to_s.match(/^\s*root\s*['"]([^'"]*)['"]/)[1]
        end
      end

      def self.add_route(route)

        file_path = 'config/routes.rb'

        # Read the file content
        content = File.read(file_path)

        # Split content into lines
        lines = content.lines

        # Find the index of Rails.application.routes.draw do
        ind = -1
        lines.each_with_index do |line, i|
          if line.match?(/^\s*Rails.application.routes.draw\s*do[\s\S]+$/)
            ind = i
          end
        end

        # Insert

        if ind >= 0
          lines.insert(ind + 1, route)
        else
          raise "ERROR: Could not find Rails.application.routes.draw do"
        end

        # Write the modified content back to the file
        begin
          File.write(file_path, lines.map { |l| l.gsub(/\n/, '') }.join("\n"))
          puts "Successfully inserted «root '#{route}'» into '#{file_path}'"
        rescue => e
          raise "Error writing to #{file_path} => «#{e.message}»"
        end

      end

      def self.route_exists?(target_route)

        # check if exists
        # Ensure the Rails environment is loaded
        # require File.expand_path("../config/environment", __dir__)

        # Get all routes from the Rails application
        routes = Rails.application.routes.routes

        # Check if the route exists
        routes.any? do |route|
          # Extract the path spec and remove any optional parts or constraints
          path = route.path.spec.to_s
          # Clean up the path to match the format (remove leading/trailing slashes, etc.)
          cleaned_path = path.gsub(/\(.*\)/, "").gsub(/^\/|\/$/, "")
          # Check if the cleaned path matches the target route
          cleaned_path == target_route
        end

      end

      def self.remove_files(file_paths)
        file_paths.each do |f|
          if File.exist?(f)
            if File.directory?(f)
              Dir.delete(f)
              puts "  • Removed directory: #{f}"
            else
              File.delete(f)
              puts "  • Removed file: #{f}"
            end
          else
            puts "  • File/Path not found so not removed: #{f}"
          end
        end
      end

      def self.remove_line_from_file(file_path, string_to_find)

        # Read the file content
        content = File.read(file_path)

        # Split content into lines
        lines = content.lines

        found_lines = []
        modified_content = []
        lines.each do |line|
          if line.match(/^[\s\S]+#{string_to_find}[\s\S]+$/)
            found_lines.push(line)
          else
            modified_content.push(line)
          end
        end

        utils = SvelteOnRails::Installer::Utils
        if found_lines.present? && utils.ask_yn("Remove lines\n#{found_lines.join("\n  • ")}\n from #{file_path}?")
          # Write the modified content back to the file
          begin
            File.write(file_path, modified_content.map{|l|l.gsub(/\n/, '')}.join("\n"))
            puts "Successfully removed #{found_lines.length} #{'line'.pluralize(found_lines.length)}."
          rescue => e
            raise "Error writing to #{file_path} => «#{e.message}»"
          end
        else


        end

      end

    end
  end
end