require 'multi_json'
require 'aws-sdk-route53'

module Itamae
  module Resource
    class Route53Record < File
      define_attribute :action, default: :create
      define_attribute :region, type: String, required: true
      define_attribute :profile, type: String, default: 'default'
      define_attribute :hosted_zone_id, type: String, required: true
      define_attribute :comment, type: String
      define_attribute :record_name, type: String, default_name: true
      define_attribute :type, type: String, required: true
      define_attribute :ttl, type: Integer, required: true
      define_attribute :value, type: [ String, Array ], required: true
      # define_attribute :set_identifier, type: String
      # define_attribute :weight, type: String
      # define_attribute :failover, type: String
      # define_attribute :health_check_id, type: String
      # define_attribute :traffic_policy_instance_id, type: String

      def pre_action
        attributes.record_name = attributes.record_name + '.' unless attributes.record_name[-1] == '.'
        credentials = Aws::SharedCredentials.new(profile_name: attributes.profile)
        @route53 = Aws::Route53::Client.new(region: attributes.region, credentials: credentials)

        @change_batch = define_change_batch
        resource_record_set = @change_batch[:changes][0][:resource_record_set]
        attributes.content = MultiJson.dump(resource_record_set, pretty: true)

        @resource_record_set = compare_record_values(fetch_record)

        case @current_action
        when :create
          attributes.exist = true
        when :upsert
          attributes.exist = true
        when :delete
          attributes.exist = false
        end

        send_tempfile
        compare_file if @current_action == :upsert
      end

      def set_current_attributes
        current.modified = false
      end

      def action_create
        return if current.exist
        @route53.change_resource_record_sets(
          change_batch: @change_batch,
          hosted_zone_id: attributes.hosted_zone_id
        )
      end

      def action_upsert
        @route53.change_resource_record_sets(
          change_batch: @change_batch,
          hosted_zone_id: attributes.hosted_zone_id
        )
      end

      def action_delete
        return unless current.exist
        @route53.change_resource_record_sets(
          change_batch: @change_batch,
          hosted_zone_id: attributes.hosted_zone_id
        )
      rescue Aws::Route53::Errors::InvalidChangeBatch => e
        Itamae.logger.warn e.inspect
      end

      private

      def define_change_batch
        if attributes.value.class == Array
          resource_records = attributes.value.map do |v|
            { value: v }
          end
        elsif attributes.value.class == String
          resource_records = Array.new(1, { value: attributes.value })
        end

        resource_record_set = {
          name: attributes.record_name,
          type: attributes.type,
          ttl: attributes.ttl,
          resource_records: resource_records
        }

        changes = {
          action: attributes.action.to_s.upcase,
          resource_record_set: resource_record_set
        }

        {
          changes: [ changes ],
          comment: attributes.comment
        }
      end

      def fetch_record
        resp = @route53.list_resource_record_sets(
          hosted_zone_id: attributes.hosted_zone_id,
          start_record_name: attributes.record_name,
          start_record_type: attributes.type,
          start_record_identifier: attributes.set_identifier,
          max_items: 1
        )

        resp.resource_record_sets[0]
      end

      def compare_record_values(resource_record_set)
        if attributes.record_name == resource_record_set.name && attributes.type == resource_record_set.type
          current.exist = true
          resource_record_set.to_h
        else
          current.exist = false
          {}
        end
      end

      def compare_to
        if current.exist
          f = Tempfile.open('itamae')
          f.write(MultiJson.dump(@resource_record_set, pretty: true))
          f.close
          f.path
        else
          '/dev/null'
        end
      end

      def show_content_diff
        if attributes.modified
          Itamae.logger.info 'Convert resource record set to JSON and display the difference.'
        end

        super
      end
    end
  end
end