# frozen_string_literal: true

module MuxTf
  module Cli
    module Current
      extend TerraformHelpers
      extend PiotrbCliUtils::Util
      extend PiotrbCliUtils::CriCommandSupport
      extend PiotrbCliUtils::CmdLoop

      PLAN_FILENAME = 'foo.tfplan'

      class << self
        def run(args)
          version_check

          if args[0] == 'cli'
            cmd_loop
            return
          end

          folder_name = File.basename(Dir.getwd)
          log "Processing #{Paint[folder_name, :cyan]} ..."

          ENV['TF_IN_AUTOMATION'] = '1'
          ENV['TF_INPUT'] = '0'

          return launch_cmd_loop(:error) unless run_validate

          if ENV['TF_UPGRADE']
            upgrade_status, upgrade_meta = run_upgrade
            return launch_cmd_loop(:error) unless upgrade_status == :ok
          end

          plan_status, @plan_meta = create_plan(PLAN_FILENAME)

          case plan_status
          when :ok
            log 'no changes, exiting', depth: 1
          when :error
            log 'something went wrong', depth: 1
            launch_cmd_loop(plan_status)
          when :changes
            log 'Printing Plan Summary ...', depth: 1
            pretty_plan_summary(PLAN_FILENAME)
            launch_cmd_loop(plan_status)
          when :unknown
            launch_cmd_loop(plan_status)
          end
        rescue Exception => e # rubocop:disable Lint/RescueException
          puts Paint['Unhandled Exception!', :red]
          puts '=' * 20
          puts e.full_message
          puts
          puts '< press enter to continue >'
          gets
          exit 1
        end

      private

        def version_check
          if VersionCheck.has_updates?
            log Paint["="*80, :yellow]
            log "New version of #{Paint["mux_tf", :cyan]} is available!"
            log "You are currently on version: #{Paint[VersionCheck.current_gem_version, :yellow]}"
            log "Latest version found is: #{Paint[VersionCheck.latest_gem_version, :green]}"
            log "Run `#{Paint["gem update muf_tf", :green]}` to update!"
            log Paint["="*80, :yellow]
          end
        end

        def run_validate
          remedies = PlanFormatter.process_validation(validate)
          process_remedies(remedies)
        end

        def process_remedies(remedies)
          if remedies.delete? :init
            log 'Running terraform init ...', depth: 2
            tf_init
            remedies = PlanFormatter.process_validation(validate)
            process_remedies(remedies)
          end
          unless remedies.empty?
            log "unprocessed remedies: #{remedies.to_a}", depth: 1
            return false
          end
          true
        end

        def validate
          log 'Validating module ...', depth: 1
          tf_validate.parsed_output
        end

        def create_plan(filename)
          log 'Preparing Plan ...', depth: 1
          exit_code, meta = PlanFormatter.pretty_plan(filename)
          case exit_code
          when 0
            [:ok, meta]
          when 1
            [:error, meta]
          when 2
            [:changes, meta]
          else
            log Paint["terraform plan exited with an unknown exit code: #{exit_code}", :yellow]
            [:unknown, meta]
          end
        end

        def launch_cmd_loop(status)
          return if ENV['NO_CMD']

          case status
          when :error, :unknown
            log Paint['Dropping to command line so you can fix the issue!', :red]
          when :changes
            log Paint['Dropping to command line so you can review the changes.', :yellow]
          end
          cmd_loop(status)
        end

        def cmd_loop(status = nil)
          root_cmd = build_root_cmd

          folder_name = File.basename(Dir.getwd)

          puts root_cmd.help

          prompt = "#{folder_name} => "
          case status
          when :error, :unknown
            prompt = "[#{Paint[status.to_s, :red]}] #{prompt}"
          when :changes
            prompt = "[#{Paint[status.to_s, :yellow]}] #{prompt}"
          end

          run_cmd_loop(prompt) do |cmd|
            throw(:stop, :no_input) if cmd == ''
            args = Shellwords.split(cmd)
            root_cmd.run(args, {}, hard_exit: false)
          end
        end

        def build_root_cmd
          root_cmd = define_cmd(nil)

          root_cmd.add_command(plan_cmd)
          root_cmd.add_command(apply_cmd)
          root_cmd.add_command(shell_cmd)
          root_cmd.add_command(force_unlock_cmd)
          root_cmd.add_command(upgrade_cmd)
          root_cmd.add_command(interactive_cmd)

          root_cmd.add_command(exit_cmd)
          root_cmd
        end

        def plan_cmd
          define_cmd('plan', summary: 'Re-run plan') do |_opts, _args, _cmd|
            run_validate && run_plan
          end
        end

        def apply_cmd
          define_cmd('apply', summary: 'Apply the current plan') do |_opts, _args, _cmd|
            status = tf_apply(filename: PLAN_FILENAME)
            if status.success?
              throw :stop, :done
            else
              log 'Apply Failed!'
            end
          end
        end

        def shell_cmd
          define_cmd('shell', summary: 'Open your default terminal in the current folder') do |_opts, _args, _cmd|
            log Paint['Launching shell ...', :yellow]
            log Paint['When it exits you will be back at this prompt.', :yellow]
            system ENV['SHELL']
          end
        end

        def force_unlock_cmd
          define_cmd('force-unlock', summary: 'Force unlock state after encountering a lock error!') do
            prompt = TTY::Prompt.new(interrupt: :noop)

            table = TTY::Table.new(header: %w[Field Value])
            table << ['Lock ID', @plan_meta['ID']]
            table << ['Operation', @plan_meta['Operation']]
            table << ['Who', @plan_meta['Who']]
            table << ['Created', @plan_meta['Created']]

            puts table.render(:unicode, padding: [0, 1])

            if @plan_meta && @plan_meta['error'] == 'lock'
              done = catch(:abort) do
                if @plan_meta['Operation'] != 'OperationTypePlan'
                  throw :abort unless prompt.yes?(
                    "Are you sure you want to force unlock a lock for operation: #{@plan_meta['Operation']}",
                    default: false
                  )
                end

                throw :abort unless prompt.yes?(
                  'Are you sure you want to force unlock this lock?',
                  default: false
                )

                status = tf_force_unlock(id: @plan_meta['ID'])
                if status.success?
                  log 'Done!'
                else
                  log Paint["Failed with status: #{status}", :red]
                end

                true
              end

              log Paint['Aborted', :yellow] unless done
            else
              log Paint['No lock error or no plan ran!', :red]
            end
          end
        end

        def upgrade_cmd
          define_cmd('upgrade', summary: 'Upgrade modules/plguins') do |_opts, _args, _cmd|
            status, meta = run_upgrade
            if status != :ok
              log meta.inspect unless meta.empty?
              log 'Upgrade Failed!'
            end
          end
        end

        def interactive_cmd
          define_cmd('interactive', summary: 'Apply interactively') do |_opts, _args, _cmd|
            status = run_shell([tf_plan_summrary_cmd, PLAN_FILENAME, '-i'], return_status: true)
            if status != 0
              log 'Interactive Apply Failed!'
            else
              run_plan
            end
          end
        end

        def run_plan
          plan_status, @plan_meta = create_plan(PLAN_FILENAME)

          case plan_status
          when :ok
            log 'no changes', depth: 1
          when :error
            log 'something went wrong', depth: 1
          when :changes
            log 'Printing Plan Summary ...', depth: 1
            pretty_plan_summary(PLAN_FILENAME)
          when :unknown
            # nothing
          end
        end

        def run_upgrade
          exit_code, meta = PlanFormatter.process_upgrade
          case exit_code
          when 0
            [:ok, meta]
          when 1
            [:error, meta]
          # when 2
          #   [:changes, meta]
          else
            log Paint["terraform init upgrade exited with an unknown exit code: #{exit_code}", :yellow]
            [:unknown, meta]
          end
        end

        def tf_plan_summrary_cmd
          @tf_plan_summrary_cmd ||= File.expand_path(File.join(__dir__, '..', '..', '..', 'exe', 'tf_plan_summary'))
        end

        def pretty_plan_summary(filename)
          run_with_each_line([tf_plan_summrary_cmd, filename]) do |raw_line|
            log raw_line.rstrip, depth: 2
          end
        end
    end
  end
  end
end