# frozen_string_literal: true

module Gitlab
  module ImportExport
    module AfterExportStrategies
      class BaseAfterExportStrategy
        extend Gitlab::ImportExport::CommandLineUtil
        include ActiveModel::Validations
        extend Forwardable

        StrategyError = Class.new(StandardError)

        private

        attr_reader :project, :current_user, :lock_file

        public

        def initialize(attributes = {})
          @options = OpenStruct.new(attributes)

          self.class.instance_eval do
            def_delegators :@options, *attributes.keys
          end
        end

        def execute(current_user, project)
          @project = project

          ensure_export_ready!
          ensure_lock_files_path!
          @lock_file = File.join(lock_files_path, SecureRandom.hex)
          @current_user = current_user

          if invalid?
            log_validation_errors

            return
          end

          create_or_update_after_export_lock
          strategy_execute

          true
        rescue StandardError => e
          project.import_export_shared.error(e)
          false
        ensure
          delete_after_export_lock
          delete_export_file
          delete_archive_path
        end

        def to_json(options = {})
          @options.to_h.merge!(klass: self.class.name).to_json
        end

        def ensure_export_ready!
          raise StrategyError unless project.export_file_exists?
        end

        def ensure_lock_files_path!
          FileUtils.mkdir_p(lock_files_path) unless Dir.exist?(lock_files_path)
        end

        def lock_files_path
          project.import_export_shared.lock_files_path
        end

        def archive_path
          project.import_export_shared.archive_path
        end

        def locks_present?
          project.import_export_shared.locks_present?
        end

        protected

        def strategy_execute
          raise NotImplementedError
        end

        def delete_export?
          true
        end

        private

        def delete_export_file
          return if locks_present? || !delete_export?

          project.remove_exports
        end

        def delete_archive_path
          FileUtils.rm_rf(archive_path) if File.directory?(archive_path)
        end

        def create_or_update_after_export_lock
          FileUtils.touch(lock_file)
        end

        def delete_after_export_lock
          FileUtils.rm(lock_file) if lock_file.present? && File.exist?(lock_file)
        end

        def log_validation_errors
          errors.full_messages.each { |msg| project.import_export_shared.add_error_message(msg) }
        end
      end
    end
  end
end
