#!/usr/bin/env ruby

require 'logger'
require 'open3'

module OnApp
  LOG_PATH = "/onapp/system_agent/log/commands.log".freeze
  UTILITIES = %w{ systemctl clusterctl }.freeze
  SYSTEMCTL = 'systemctl'.freeze
  CLUSTERCTL = 'clusterctl'.freeze
  SCRIPT_NAME = $0.split('/').last.freeze

  class Systemctl
    BASE_TOOLS = %w{ service }.freeze

    attr_reader :tool, :args

    def initialize(args)
      usage if (args.last.eql? 'help' || args.size > 3)

      @args = args
      @tool = args.shift
      usage unless tools.include?(tool)

      use_tool
    end

    private

    def utility
      SYSTEMCTL
    end

    def use_tool
      ServiceMgmt.new(utility, tool, args)
    end

    def tools
      BASE_TOOLS
    end

    def tools_help
      "service"
    end

    def usage
      puts "Usage: #{SCRIPT_NAME} #{utility} #{tools_help} <command> <name>"
      exit 0
    end
  end

  class BaseMgmt
    BASE_COMMANDS = %w{ start stop restart }.freeze
    SERVICES = %w{ crond lsyncd httpd nfs dhcpd xinetd onapp-engine onapp-vnc-proxy onapp-ssh-agent }.freeze

    attr_reader :utility, :tool, :tool_name, :command, :logger

    def initialize(utility, tool, args)
      @utility = utility
      @tool = tool
      @command = args.shift
      @tool_name = args.shift
      @logger = ::Logger.new(LOG_PATH, 10, 1*1024*1024)
      validate_command
      validate_tool_name

      execute
    end

    private

    def execute
      Open3.popen2e(system_command) do |_, stdout_err, wait_thr|
        exit_status = wait_thr.value
        output = stdout_err.readlines.join('')

        if exit_status.success?
          logger.info(output)
        else
          $stderr.puts("Error. #{output}")
          logger.error("Error. #{output}")
        end
      end
    end

    def validate_command
      usage if command.nil?
      unsupported(:command, command) unless commands.include?(command)
    end

    def validate_tool_name
      usage if tool_name.nil?
      unsupported(tool, tool_name) unless tools.include?(tool_name)
    end

    def commands
      BASE_COMMANDS
    end

    def tools
      SERVICES
    end

    def usage
      puts "Usage: #{SCRIPT_NAME} {#{ commands.join("|") }} {#{ SERVICES.join("|") }}"
      exit 0
    end

    def unsupported(type, tool_name)
      message = "#{tool_name}: unsupported #{type}"
      $stderr.puts message
      logger.error("Error. #{message}")
      exit 1
    end
  end

  class ServiceMgmt < BaseMgmt
    def system_command
      "sudo service #{tool_name} #{command}"
    end
  end

  class Clusterctl < Systemctl
    CLUSTER_TOOLS = (BASE_TOOLS.dup << 'node').freeze

    CLUSTER_RESOURCES_COMMANDS = %w{ manage unmanage }
    NODE_COMMANDS = %w{ online standby maintenance ready }.freeze

    def initialize(args)
      super
    end

    private

    def utility
      CLUSTERCTL
    end

    def use_tool
      if tool.eql? 'service'
        ServiceMgmt.new(utility, tool, args)
      else
        NodeMgmt.new(utility, tool, args)
      end
    end

    def tools
      (BASE_TOOLS + CLUSTER_TOOLS).uniq
    end

    def tools_help
      "{#{ tools.join("|") }}"
    end

    class ServiceMgmt < BaseMgmt
      def system_command
        "sudo crm resource #{command} #{tool_name}"
      end

      def commands
        (BASE_COMMANDS + CLUSTER_RESOURCES_COMMANDS).uniq
      end
    end

    class NodeMgmt < BaseMgmt
      def system_command
        "sudo crm node #{command} #{tool_name}"
      end

      def commands
        (NODE_COMMANDS).uniq
      end

      def validate_tool_name
        usage if tool_name.nil?
      end

      def usage
        puts "Usage: #{SCRIPT_NAME} {#{ commands.join("|") }} <node_name>"
        exit 0
      end
    end
  end

  class SysAgent
    attr_reader :args, :logger, :utility

    def initialize(args)
      init_logger
      logger.info(args.join(' '))

      usage if args.empty?
      @args = args
      @utility = args.shift

      usage if utility.nil? || !(UTILITIES.include?(utility))
      usage if args.first.eql?('help')
    end

    def execute
      case utility
        when SYSTEMCTL
          Systemctl.new(args)
        when CLUSTERCTL
          Clusterctl.new(args)
        else
          usage
      end
    end

    private

    def init_logger
      %x[ mkdir -p #{LOG_PATH.split("/").tap(&:pop).join("/")} ]
      @logger = ::Logger.new(LOG_PATH, 10, 1*1024*1024)
    end

    def usage
      puts "Usage: #{SCRIPT_NAME} {#{ UTILITIES.join("|") }} <tool> <command> <name>"
      exit 0
    end
  end
end

OnApp::SysAgent.new(ARGV).execute