class MiqWorker
  module SystemdCommon
    extend ActiveSupport::Concern

    class_methods do
      def service_base_name
        "manageiq-#{minimal_class_name.underscore.tr("/", "_")}"
      end

      def service_file
        "#{service_base_name}@.service"
      end

      def target_file
        "#{service_base_name}.target"
      end

      def systemd_unit_dir
        Pathname.new("/lib/systemd/system")
      end
    end

    def unit_name
      "#{service_base_name}#{unit_instance}.service"
    end

    def start_systemd_worker
      enable_systemd_unit
      write_unit_settings_file
      start_systemd_unit
    end

    def stop_systemd_worker
      stop_systemd_unit
      cleanup_unit_settings_file
      disable_systemd_unit
    end

    def enable_systemd_unit(runtime: false, replace: true)
      systemd.EnableUnitFiles([unit_name], runtime, replace)
    end

    def disable_systemd_unit(runtime: false)
      systemd.DisableUnitFiles([unit_name], runtime)
    end

    def start_systemd_unit(mode: "replace")
      systemd.StartUnit(unit_name, mode)
    end

    def stop_systemd_unit(mode: "replace")
      systemd.StopUnit(unit_name, mode)
    end

    def sd_notify_started
      sd_notify.ready
    end

    def sd_notify_stopping
      sd_notify.stopping
    end

    def sd_notify_watchdog
      sd_notify.watchdog
    end

    def sd_notify_watchdog_usec(timeout_in_seconds)
      usec = timeout_in_seconds * 1_000_000
      sd_notify.notify("WATCHDOG_USEC=#{usec}", false)
    end

    private

    def systemd
      @systemd ||= begin
        require "dbus/systemd"
        DBus::Systemd::Manager.new
      end
    end

    def sd_notify
      @sd_notify ||= begin
        require "sd_notify"
        SdNotify
      end
    end

    def service_base_name
      self.class.service_base_name
    end

    def unit_instance
      "@#{guid}"
    end

    def write_unit_settings_file
      FileUtils.mkdir_p(unit_config_path)           unless unit_config_path.exist?
      unit_config_file_path.write(unit_config_file) unless unit_config_file_path.exist?
    end

    def cleanup_unit_settings_file
      unit_config_file_path.delete if unit_config_file_path.exist?
      unit_config_path.delete      if unit_config_path.exist?
    end

    def unit_config_name
      "#{unit_name}.d"
    end

    def unit_config_path
      self.class.systemd_unit_dir.join(unit_config_name)
    end

    def unit_config_file_path
      unit_config_path.join("override.conf")
    end

    def unit_config_file
      environment = systemd_environment_variables.merge(environment_variables)

      <<~UNIT_CONFIG_FILE
        [Service]
        #{unit_settings.compact.map { |key, value| "#{key}=#{value}" }.join("\n")}
        #{environment.compact.map   { |key, value| "Environment=#{key}=#{value}" }.join("\n")}
      UNIT_CONFIG_FILE
    end

    # Override this in a sub-class if the specific instance needs
    # any additional configuration settings
    def unit_settings
      {
        "MemoryHigh"      => worker_settings[:memory_threshold]&.bytes,
        "TimeoutStartSec" => worker_settings[:starting_timeout],
        "TimeoutStopSec"  => worker_settings[:stopping_timeout],
        "WatchdogSec"     => worker_settings[:heartbeat_timeout]
      }
    end

    # Override this in a child class to add environment variables
    def systemd_environment_variables
      {}
    end
  end
end
