# frozen_string_literal: true

require 'shellwords'
require 'open3'
require 'test/unit'

class Command
  def exec(cmd='', args={})
    env.each { |k, v| ENV[k.to_s] = v.to_s }
    line = build_line(cmd, args)
    Open3.capture3(line)
  end

  def env
    {}
  end

  def default_args
    {}
  end

  def executable
    raise 'Subclasses must override this method'
  end

  private

  def build_line(cmd='', args={})
    [].tap { |line|
      line << executable
      line << default_args.merge(args).map { |k, v| "#{Shellwords.escape(k.strip)}=#{Shellwords.escape(v.strip)}" }.join(' ')
      line << cmd.strip
      line.reject!(&:empty?)
    }.join(' ')
  end

  def overrides(env)
    intersection = env.keys.to_set & ENV.keys.to_set
    ENV.select { |k, v| intersection.include?(k) }
  end
end

class TaskWarriorCommand < Command
  attr_accessor :data_dir

  def version
    exec('_version')
  end

  def count
    exec('count')
  end

  def env
    raise "data_dir must not be empty for '#{executable}'" if @data_dir.nil? || data_dir.empty?
    { TASKDATA: @data_dir }
  end

  def default_args
    { 'rc.verbose' => 'nothing', 'rc.json.array' => 'on' }
  end

  def executable
    'task'
  end
end

class TaskUUID < TaskWarriorCommand
  def create(description)
    exec(description)
  end

  def executable
    # The gem version that uses the binstub fails if the script is not Ruby
    # 'task-uuid'

    # The local version, called directly, works fine even if it's a Bash script
    File.join(File.dirname(__FILE__), '..', '..', 'bin', 'task-uuid')
  end

  def default_args
    {}
  end
end

class TestTaskUUID < Test::Unit::TestCase
  def setup
    @tw = TaskWarriorCommand.new
    @tw.data_dir = Dir.mktmpdir(name)

    raise "TASKRC must not be set, but it is #{ENV['TASKRC']}" if ENV['TASKRC']
  end

  def teardown
    FileUtils.rm_rf(@tw.data_dir)
  end

  def test_version
    out, err, status = @tw.version
    assert(status.success?)
    assert_empty(err)
    assert_not_empty(out)
    assert_match(/\d\.\d\.\d/, out.chomp)
  end

  def test_empty
    out, err, status = @tw.count
    assert(status.success?)
    assert_empty(err)
    assert_not_empty(out)
    assert_equal('0', out.chomp)
  end

  def test_task_uuid
    task_uuid = TaskUUID.new
    task_uuid.data_dir = Dir.mktmpdir(name)
    out, err, _ = task_uuid.create('foo bar')
    assert_empty(err)
    assert_not_empty(out)
    assert_match(/[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/, out.chomp)
  end
end
