module Terraspace
  class Check
    extend Memoist

    attr_reader :min_terraform_version, :max_terraform_version
    def initialize(options={})
      @options = options
      # Terraspace requires this terraform version or a fork
      @min_terraform_version = "0.12"
      @max_terraform_version = "1.5.7"
    end

    # Used for the CLI
    def run(cloud: false)
      puts "terraspace version: #{Terraspace::VERSION}"
      if terraform_bin
        puts "#{terraform_bin_name} bin: #{pretty_home(terraform_bin)}"
        puts "#{terraform_bin_name} version: #{terraform_version}"
        # check for allowed version of terraform
        if allowed_terraform_version?
          puts "You're all set!".color(:green)
        else
          error_message(cloud: cloud)
          exit 1
        end
      else
        puts terraform_is_not_installed
        exit 1
      end
    end
    alias ok! run

    def error_message(cloud: false)
      name = cloud ? "Terraspace Cloud" : "Terraspace"
      cloud_note =<<~EOL

        Note: If you're using Terraspace Cloud, you won't be able to bypass this check.
        See: https://terraspace.cloud/docs/terraform/license/
      EOL

      puts "ERROR: #{name} requires Terraform between #{@min_terraform_version}.x and #{@max_terraform_version}".color(:red)
      puts <<~EOL
        This is because newer versions of Terraform have a BSL license
        If your usage is allowed by the license, you can bypass this check with:

            export TS_VERSION_CHECK=0
        #{cloud_note}
      EOL
    end

    # aliased with ok?
    def allowed_terraform_version?
      return true if ENV['TS_VERSION_CHECK'] == '0' && !Terraspace.cloud?
      return false unless terraform_bin
      return true if !terraform_bin.include?("terraform") # IE: allow any version of terraform forks

      major, minor, patch = terraform_version.split('.').map(&:to_i)
      min_major, min_minor, _ = @min_terraform_version.split('.').map(&:to_i)
      max_major, max_minor, max_patch = @max_terraform_version.split('.').map(&:to_i)

      # must be between min and max
      min_ok = major > min_major ||
              (major == min_major && minor >= min_minor)
      max_ok = major < max_major ||
              (major == max_major && minor < max_minor) ||
              (major == max_major && minor == max_minor && patch <= max_patch)

      min_ok && max_ok
    end
    alias ok? allowed_terraform_version?

    def terraform_is_not_installed
      <<~EOL
        Terraform not installed. Unable to detect a terraform command.
        Please double check that an allowed version of terraform is installed.
        See: https://terraspace.cloud/docs/install/terraform/
      EOL
    end

    BINS = %w[tofu terraform]
    @@terraform_bin = nil
    def terraform_bin
      return ENV['TS_TERRAFORM_BIN'] if ENV['TS_TERRAFORM_BIN']
      return @@terraform_bin if @@terraform_bin.present?
      BINS.each do |bin|
        if Gem.win_platform?
          out = "is #{bin}.exe".strip
        else
          out = `type #{bin} 2>&1`.strip
          next unless $?.success?
        end
        md = out.match(/is (.*)/)
        if md
          @@terraform_bin = md[1]
          break
        end
      end
      @@terraform_bin
    end
    memoize :terraform_bin

    def terraform_bin_name
      terraform_bin ? File.basename(terraform_bin) : "terraform"
    end

    # Sometimes Terraform shows the version info on the first line and sometimes on the bottom line.
    # Account for that by finding the line.
    #
    #     $ terraform --version
    #     Terraform v0.12.24
    #
    #     Your version of Terraform is out of date! The latest version
    #     is 0.12.26. You can update by downloading from https://www.terraform.io/downloads.html
    #
    # Another example
    #
    #     $ tofu --version
    #     OpenTofu v1.6.0
    #
    # Note: The -json option is only available in v0.13+
    def terraform_version
      out = `#{terraform_bin} --version`
      # 1st regexp is more strict to avoid false positives
      # 2nd regexp is more lenient in include beta suffixes
      line = out.split("\n").find { |l| l =~ /(\d+\.\d+\.\d+)/ }
      version = line.match(/(\d+\.\d+\.\d+.*)/)[1] if line
    end
    memoize :terraform_version

    def terraspace_version
      Terraspace::VERSION
    end

    def versions
      {
        terraspace_version: terraspace_version,
        terraform_version: terraform_version,
        terraform_command: terraform_bin_name,
      }
    end

    def pretty_home(path)
      path.sub(ENV['HOME'], '~')
    end

    class << self
      # Used as library call
      def check!
        setup = new
        return if setup.ok?
        # run meth designed for CLI and will puts out informative messages about installed version and exit 1 when version is not ok
        setup.run
      end
    end
  end
end
