# frozen_string_literal: true

require 'diffy'
require 'deepsort'

module DatadogBackup
  class Core
    include ::DatadogBackup::LocalFilesystem
    include ::DatadogBackup::Options

    def api_service
      raise 'subclass is expected to implement #api_service'
    end

    def api_version
      raise 'subclass is expected to implement #api_version'
    end

    def api_resource_name
      raise 'subclass is expected to implement #api_resource_name'
    end

    def backup
      raise 'subclass is expected to implement #backup'
    end

    # Returns the diffy diff.
    # Optionally, supply an array of keys to remove from comparison
    def diff(id)
      current = except(get_by_id(id)).deep_sort.to_yaml
      filesystem = except(load_from_file_by_id(id)).deep_sort.to_yaml
      result = ::Diffy::Diff.new(current, filesystem, include_plus_and_minus_in_html: true).to_s(diff_format)
      logger.debug("Compared ID #{id} and found #{result}")
      result
    end

    # Returns a hash with banlist elements removed
    def except(hash)
      hash.tap do # tap returns self
        @banlist.each do |key|
          hash.delete(key) # delete returns the value at the deleted key, hence the tap wrapper
        end
      end
    end

    def get(id)
      with_200 do
        api_service.request(Net::HTTP::Get, "/api/#{api_version}/#{api_resource_name}/#{id}", nil, nil, false)
      end
    end

    def get_all
      with_200 do
        api_service.request(Net::HTTP::Get, "/api/#{api_version}/#{api_resource_name}", nil, nil, false)
      end
    end

    def get_and_write_file(id)
      write_file(dump(get_by_id(id)), filename(id))
    end

    def get_by_id(id)
      except(get(id))
    end

    def initialize(options)
      @options = options
      @banlist = []
      ::FileUtils.mkdir_p(mydir)
    end

    def myclass
      self.class.to_s.split(':').last.downcase
    end

    # Calls out to Datadog and checks for a '200' response
    def update(id, body)
      with_200 do
        api_service.request(Net::HTTP::Put, "/api/#{api_version}/#{api_resource_name}/#{id}", nil, body, true)
      end
      logger.warn 'Successfully restored to datadog.'
    end

    def with_200
      max_retries = 6
      retries ||= 0

      response = yield
      raise "Request failed with error #{response}" unless response[0] == '200'

      response[1]
    rescue ::Net::OpenTimeout => e
      if (retries += 1) <= max_retries
        sleep(0.1 * retries**5) # 0.1, 3.2, 24.3, 102.4 seconds per retry
        retry
      else
        raise "Request failed with error #{e.message}"
      end
    end

  end
end