module CiCd
  module Builder
    # noinspection RubySuperCallWithoutSuperclassInspection
    module EnvironmentsList
      CLASS = 'CiCd::Builder::EnvironmentsList'
      module Build

        alias_method :super_prepareBuild, :prepareBuild
        # ---------------------------------------------------------------------------------------------------------------
        # noinspection RubyHashKeysTypesInspection
        def prepareBuild()
          @logger.step CLASS+'::'+__method__.to_s
          # self.class.superclass.instance_method(:prepareBuild).bind(self).call()
          super_prepareBuild
          if 0 == @vars[:return_code]
            @vars[:components] = {}
            @vars[:artifacts] = []
            # Now we need to pull the current environments list
            # Get the repo a little earlier than the typical build which uses it as a write target iso a read source
            getRepoInstance('Artifactory')
            if 0 == @vars[:return_code]
              getLatestEnvironments()
            end
          end

          @vars[:return_code]
        end

        # ---------------------------------------------------------------------------------------------------------------
        def loadEnvironments(filename,version,changed=false)
          require 'hashie'
          @vars[:environments] = {
              file: filename,
              version: version,
              changed: (changed or not File.exists?(filename)),
          }
          data = IO.read(filename) rescue '{}'
          json = begin
            JSON.parse(data)
          rescue
            eval(data)
          end
          IO.write(filename, JSON.pretty_generate(json, {indent: "\t", space: ' '})) # Just to be tidy :)
          @vars[:environments][:data] = Hashie::Mash.new(json)
          @logger.info "#{ENV['ARTIFACTORY_ENVIRONMENTS_MODULE']}-#{version}"
        end

        # ---------------------------------------------------------------------------------------------------------------
        def getLatestEnvironments(oldver=nil)
          version = @repo.latestArtifactoryVersion(ENV['ARTIFACTORY_ENVIRONMENTS_MODULE'], ENV['ARTIFACTORY_RELEASE_REPO'])
          if version
            if version != oldver
              objects = @repo.maybeArtifactoryObject(ENV['ARTIFACTORY_ENVIRONMENTS_MODULE'], version, false, ENV['ARTIFACTORY_RELEASE_REPO'])
              if objects and objects.size > 0
                if objects.size > 1
                  @logger.error "Too many versions found for #{ENV['ARTIFACTORY_RELEASE_REPO']}/#{ENV['ARTIFACTORY_ENVIRONMENTS_MODULE']}-#{version} during preparation?"
                  @vars[:return_code] = Errors::ARTIFACT_MULTI_MATCH
                else
                  loadEnvironments(objects[0].download(), version)
                end
              else
                @logger.error "Version not found for #{ENV['ARTIFACTORY_RELEASE_REPO']}/#{ENV['ARTIFACTORY_ENVIRONMENTS_MODULE']}-#{version} during preparation?"
                @vars[:return_code] = Errors::ARTIFACT_NOT_FOUND
              end
            else
              @logger.info "Alread have the latest version (#{oldver})"
            end
          else
            @logger.warn "No version found for #{ENV['ARTIFACTORY_RELEASE_REPO']}/#{ENV['ARTIFACTORY_ENVIRONMENTS_MODULE']} during preparation?"
            # @vars[:return_code] = Errors::ARTIFACT_NOT_FOUND
            loadEnvironments('/tmp/environments.json', '1', true)
          end
          @vars[:return_code]
        end

        alias_method :super_cleanupBuild, :cleanupBuild
        # ---------------------------------------------------------------------------------------------------------------
        def cleanupAfterUpload()
          @logger.step CLASS+'::'+__method__.to_s
          if @vars[:environments] and @vars[:environments][:file]
            FileUtils.rm_rf(File.dirname(@vars[:environments][:file]))
          end
          0
        end

        # ---------------------------------------------------------------------------------------------------------------
        def packageBuild()
          @logger.info CLASS+'::'+__method__.to_s
          if @vars.has_key?(:environments) and not @vars[:environments].empty?
            @vars[:return_code] = 0
            # Now comes the fun part  ... get the list of profiles/credentials and use AWS SDK to discover environments
            # Let's reuse some code we already have and wrap it in a class
            begin
              require_relative 'lib/update_bucket_policy'
              helper = UpdateBucketPolicy.new
              helper.logger = @logger
              helper.options = Hashie::Mash.new(helper.options.to_hash)
              helper.options[:iniglob]  = ENV['AWS_INI_GLOB'] if ENV['AWS_INI_GLOB']
              helper.options[:inifile]  = ENV['AWS_INI_FILE'] if ENV['AWS_INI_FILE']
              helper.options[:profile]  = ENV['AWS_PROFILE']  if ENV['AWS_PROFILE']
              helper.options[:profiles] = ENV['AWS_PROFILES'] if ENV['AWS_PROFILES']
              helper.prepare_accounts
              if helper.accounts.size > 0
                helper_environments = loadCachedEnvironments()
                unless helper_environments.size > 0
                  helper.get_environments(true)
                  helper_environments = helper.environments
                  saveCachedEnvironments(helper_environments)
                end

                if helper_environments.size > 0
                  getLatestEnvironments(@vars[:environments][:version])
                  if 0 == @vars[:return_code]
                    environments = Hashie::Mash.new(@vars[:environments][:data])
                    exclude_regex = ENV['ENVS_EXCLUDE_REGEX'] || '-repo'
                    helper_environments.select{|e,_| e !~ /#{exclude_regex}/ }.each do |envnam,_|
                      environments[envnam] ||= {}
                    end
                    if environments.size != @vars[:environments][:data].size
                      IO.write(@vars[:environments][:file], JSON.pretty_generate(environments.to_hash, {indent: "\t", space: ' '}))
                      @vars[:environments][:changed] = true
                    end
                  end
                end
              else
                @logger.error 'No AWS accounts found during preparation?'
                @vars[:return_code] = Errors::NO_ACCOUNTS
              end
            rescue => e
              @logger.fatal "#{e.message}\n#{e.backtrace}"
              @vars[:return_code] = Errors::NO_ACCOUNTS
            end
          else
            @logger.error 'No components found during preparation?'
            @vars[:return_code] = Errors::NO_COMPONENTS
          end
          @vars[:return_code]
        end

        protected

        CACHE_FILE = '/tmp/aws_cloudformation_environments_stacks.json'
        # ---------------------------------------------------------------------------------------------------------------
        def loadCachedEnvironments
          hash = {}
          # if File.exists?(CACHE_FILE)
          # end
          begin
            stat = File.stat(CACHE_FILE)
            age  = (Time.now - stat.mtime)
            @logger.info "Environment stacks cache #{CACHE_FILE} age is #{age}s"
            if age < 3600
              hash = JSON.parse(IO.read(CACHE_FILE))
            else
              hash = {}
            end
          rescue
            hash = {}
          end
          hash
        end

        # ---------------------------------------------------------------------------------------------------------------
        def saveCachedEnvironments(environments)
          IO.write(CACHE_FILE,JSON.pretty_generate(environments,{ indent: "\t", space: ' '}))
        end

        def prepare_accounts
          @accounts = []
          if @options[:iniglob]
            iniglob = File.expand_path(@options[:iniglob])
            Dir.glob( iniglob) {| filename |
              @accounts << IniFileCredentials.new(filename)
            }
          elsif @options[:inifile]
            @accounts << IniFileCredentials.new(@options[:inifile]) unless (@options[:inifile].nil? or @options[:inifile].empty?)
          end

          if @options[:profiles]
            @options[:profiles].each {| profile |
              @accounts << ProfileCredentials.new(profile)
            }
          end
          unless @accounts.size > 0
            parse_options
            logger.fatal 'No options allow for account identification'
          end
          # Make the options mutable
          opts = @options.dup
          @options = Hash.new()
          opts.each do |k,v|
            @options[k.to_sym] = v
          end
        end

      end
    end
  end
end
