# frozen_string_literal: true

require 'open3'
require 'pty'

class Kubectl
  CAT_MULTILINE = 'EOFCONFIG'
  KUBECTL_OPTIONS = Kuberun::CLI::BASE_KUBECTL_OPTIONS.keys.map(&:to_s)

  def load_options(options)
    self.kubectl_options = parsed_options(options)
  end

  def auth_check(verb, resource:, resource_name: nil)
    cmd.run(kubectl_base_command("auth can-i #{verb}", resource: resource, resource_name: resource_name))
  end

  def get(resource:, resource_name: nil, options: nil)
    options = "#{options} --output=json"
    parsed_json do
      cmd.run(kubectl_base_command('get', resource: resource, resource_name: resource_name, options: options)).out
    end
  end

  def create(configuration:, options: nil)
    cmd.run(kubectl_base_input_command('create', configuration: configuration, options: options))
  end

  def exec(pod:, command:)
    old_state = `stty -g`

    PTY.spawn("#{kubectl_base_command('exec', resource: pod)} #{command}") do |o, i, pid|
      t_in = Thread.new do
        until i.closed? do
          input = $stdin.getch
          i.write(input)
          i.flush
        end
      end

      t_out = Thread.new do
        begin
          until o.eof? do
            $stdout.print(o.readchar)
            $stdout.flush
          end
        rescue Errno::EIO, EOFError
          nil
        end
      end

      t_in.run

      Process::waitpid(pid) rescue nil
      # "rescue nil" is there in case process already ended.

      t_out.join
      t_in.kill
      sleep 0.1
    ensure
      t_out&.kill
      t_in&.kill
      $stdout.puts
      $stdout.flush
      system "stty #{ old_state }"
    end
  end

  def delete(resource:, resource_name:)
    cmd.run(kubectl_base_command('delete', resource: resource, resource_name: resource_name))
  end

  private

  attr_accessor :kubectl_options

  def cmd(tty_options = {})
    TTY::Command.new(tty_options)
  end

  def kubectl_base_command(verb, resource:, resource_name: nil, options: nil)
    base = "#{kubectl} #{options} #{verb} #{resource}"
    base = "#{base}/#{resource_name}" if resource_name
    base
  end

  def kubectl_base_input_command(verb, configuration:, options:)
    "cat << '#{CAT_MULTILINE}' | #{kubectl} #{options} #{verb} -f - 2>&1\n#{configuration.to_json}\n#{CAT_MULTILINE}"
  end

  def kubectl
    "kubectl #{kubectl_options}"
  end

  def parsed_options(options)
    options.each_with_object([]) do |(option_name, option_value), arr|
      next if !KUBECTL_OPTIONS.include?(option_name) || option_value == ''

      arr << "--#{option_name}=#{option_value}"
    end.join(' ')
  end

  def parsed_json
    JSON.load(yield)
  end
end