# frozen_string_literal: true

require 'json'
require 'yaml'
require 'open3'

require 'jq/extend'
require 'minitest-implicit-subject'
require "minitest/reporters"
require 'minitest/autorun'
require 'minitest/pride'

class HelmCompileError < StandardError
end

class HelmDepsError < StandardError
end

class Chart
  attr_reader :name, :path, :values

  def initialize(chart)
    @name = chart.split('/').last

    @path = File.expand_path(chart)

    @values = default_values

    update_deps!
  end

  def update_deps!
    command = "helm dep update '#{path}'"
    stdout, stderr, status = Open3.capture3(command)
    raise HelmDepsError, stderr if status != 0
  end

  def reset!
    @values = default_values
    @parsed_resources = nil
  end

  def value(value)
    values.merge!(value)
  end

  def configure_custom_name(name)
    @name = name
  end

  def execute_helm_template!
    file = Tempfile.new(name)
    file.write(JSON.parse(values.to_json).to_yaml)
    file.close

    begin
      command = "helm template '#{name}' '#{path}' --namespace='default' --values='#{file.path}'"
      stdout, stderr, status = Open3.capture3(command)

      raise HelmCompileError, stderr if status != 0

      stdout
    ensure
      file.unlink
    end
  end

  def parsed_resources
    @parsed_resources ||= begin
      output = execute_helm_template!
      puts output if ENV.fetch('DEBUG', 'false') == 'true'
      YAML.load_stream(output)
    end
  end

  def resources(matcher = nil)
    return parsed_resources unless matcher

    parsed_resources.select do |r|
      r >= Hash[matcher.map { |k, v| [k.to_s, v] }]
    end
  end

  def default_values
    {
    }
  end
end

class ExtendedMinitest < Minitest::Test
  extend MiniTest::Spec::DSL
end

class ChartTest < ExtendedMinitest
  Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new

  before do
    chart.reset!
  end

  def chart
    self.class.class_variable_get('@@chart')
  end

  def resource(name)
    chart.resources(kind: name).first
  end

  def jq(matcher, object)
    value(object.jq(matcher)[0])
  end
end

class Minitest::Result
  def name
    test_name = defined?(@name) ? @name : super
    test_name.to_s.gsub /\Atest_\d{4,}_/, ""
  end
end