# frozen_string_literal: true

module Bridgetown
  module Commands
    class Build < Thor::Group
      extend BuildOptions
      extend Summarizable
      include ConfigurationOverridable

      Registrations.register do
        register(Build, "build", "build", Build.summary)
      end

      def self.banner
        "bridgetown build [options]"
      end
      summary "Build your site and save to destination folder"

      class_option :watch,
                   type: :boolean,
                   aliases: "-w",
                   desc: "Watch for changes and rebuild"

      def self.print_startup_message
        Bridgetown.logger.info "Starting:", "Bridgetown v#{Bridgetown::VERSION.magenta} " \
                                            "(codename \"#{Bridgetown::CODE_NAME.yellow}\")"
      end

      # Build your bridgetown site
      # Continuously watch if `watch` is set to true in the config.
      def build
        Bridgetown.logger.adjust_verbosity(**options)

        # @type [Bridgetown::Configuration]
        config_options = configuration_with_overrides(
          options, Bridgetown::Current.preloaded_configuration
        )

        self.class.print_startup_message unless config_options["start_command"]

        Process.setproctitle(
          "bridgetown #{Bridgetown::VERSION} " \
          "(#{config_options["start_command"] ? "start" : "build"}) [#{File.basename(Dir.pwd)}]"
        )

        config_options.run_initializers! context: :static

        if !Bridgetown.env.production? &&
            !config_options[:skip_frontend] && config_options["start_command"]
          if Bridgetown::Utils.frontend_bundler_type(config_options[:root_dir]) == :esbuild
            Bridgetown::Utils.update_esbuild_autogenerated_config config_options
          end
          invoke_frontend_watcher_from_rake
        end

        @site = Bridgetown::Site.new(config_options)

        if config_options.fetch("skip_initial_build", false)
          Bridgetown.logger.warn "Build Warning:", "Skipping the initial build. " \
                                                   "This may result in an out-of-date site."
        else
          build_site(config_options)
        end

        if config_options.fetch("watch", false)
          watch_site(config_options)
        else
          Bridgetown.logger.info "Auto-regeneration:", "disabled. Use --watch to enable."
        end
      end

      protected

      # Build your Bridgetown site
      #
      # @param options [Bridgetown::Configuration] options loaded from config and/or CLI
      def build_site(config_options)
        t = Time.now
        display_folder_paths(config_options)
        if config_options["unpublished"]
          Bridgetown.logger.info "Unpublished mode:",
                                 "enabled. Processing documents marked unpublished"
        end
        Bridgetown.logger.info "Generating…"
        @site.process
        Bridgetown.logger.info "Done! 🎉", "#{"Completed".bold.green} in less than " \
                                          "#{(Time.now - t).ceil(2)} seconds."
      end

      # Watch for file changes and rebuild the site
      #
      # @param options [Bridgetown::Configuration] options loaded from config and/or CLI
      def watch_site(config_options)
        if config_options["url"]&.include?("://localhost")
          require "socket"
          external_ip = Socket.ip_address_list.find do |ai|
            ai.ipv4? && !ai.ipv4_loopback?
          end&.ip_address
          scheme = config_options.bind&.split("://")&.first == "ssl" ? "https" : "http"
          port = config_options.port
          Bridgetown.logger.info ""
          Bridgetown.logger.info "Now serving at:", "#{scheme}://localhost:#{port}".magenta
          Bridgetown.logger.info "", "#{scheme}://#{external_ip}:#{port}".magenta if external_ip
          Bridgetown.logger.info ""
        end

        Bridgetown::Watcher.watch(@site, config_options)
      end

      # Display the source and destination folder paths
      #
      # @param options [Bridgetown::Configuration] options loaded from config and/or CLI
      def display_folder_paths(config_options)
        source = File.expand_path(config_options["source"])
        destination = File.expand_path(config_options["destination"])
        Bridgetown.logger.info "Environment:", Bridgetown.environment.cyan
        Bridgetown.logger.info "Source:", source
        Bridgetown.logger.info "Destination:", destination
        return unless config_options["plugins_dir"].is_a?(String)

        plugins_dir = File.expand_path(config_options["plugins_dir"])
        Bridgetown.logger.info "Custom Plugins:", plugins_dir if Dir.exist?(plugins_dir)
      end

      def invoke_frontend_watcher_from_rake
        require "rake"
        Rake.with_application do |rake|
          begin
            rake.raw_load_rakefile
          rescue StandardError => e
            unless e.message.include?("No Rakefile found")
              Bridgetown.logger.error "Error Running Rake:", "#{e.message} (#{e.class})"
              e.backtrace[0..1].each { |backtrace_line| Bridgetown.logger.info backtrace_line }
            end
          end
          rake["frontend:watcher"].invoke(true) if rake.tasks.any? { _1.name == "frontend:watcher" }
        end
      end
    end
  end
end
