# frozen_string_literal: true

require 'litany/resource'
require 'litany/mixins'

module Litany
  class DNSHealthCheck < Resource
    include Taggable

    cfn_type 'AWS::Route53::HealthCheck'
    tag_key 'HealthCheckTags'

    property :enable_sni, true, [true, false]
    property :failure_threshold, 3, 1..10
    property :insufficient_data_status, :unhealthy, [:healthy, :unhealthy, :last_known_status]
    property :interval, 15, 10..30
    property :inverted, false, [true, false]
    property :measure_latency, true, [true, false]
    property :target_domain, nil, [String, nil]
    property :target_ip, nil, [nil, /^((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))$|^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]+|::(ffff(:0{1,4})?:)?((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1?[0-9])?[0-9])\.){3}(25[0-5]|(2[0-4]|1?[0-9])?[0-9]))$/]
    property :target_path, nil, [nil, String]
    property :target_port, nil, [nil, 1..65_535]
    property :target_string, nil, [nil, String]
    property :type, :https, [:tcp, :http, :https, :http_str_match, :https_str_match, :cloudwatch_metric]

    resource_reference CloudWatchAlarm, :alarm, required: false

    validator :target do
      raise "You must set either alarm or target_domain and/or target_ip on #{to_s}." unless set_alarm? || set_target_domain? || set_target_ip?
      raise "You may not set both an alarm and a target_domain/target_ip on #{to_s}." if set_alarm? && (set_target_domain? || set_target_ip?)

      raise "On #{to_s} you may not set target_domain which is longer than 255 characters." if set_target_domain? && target_domain.length > 255
      raise "On #{to_s} you may not set target_path which is longer than 255 characters." if set_target_path? && target_path.length > 255

      raise "TCP checks may not have target_domain, target_path, or target_string on #{to_s}." if type == :tcp && (set_target_domain? || set_target_path? || set_target_string?)

      type :"#{type}_str_match" if set_target_string? && [:http, :https].include?(type)
      type :cloudwatch_metric if set_alarm?
    end

    def invert
      inverted true
    end

    def properties
      # TODO: Implement ChildHealthChecks, HealthThreshold
      props = {
        HealthCheckConfig: {
          Inverted: inverted,
          Type: type.upcase
        }
      }

      config = props[:HealthCheckConfig]

      if set_alarm?
        config[:AlarmIdentifier] = {
          Name: ref(alarm),
          Region: {Ref: 'AWS::Region'}
        }
        config[:InsufficientDataHealthStatus] = insufficient_data_status.pascal_case
      else
        config[:EnableSNI] = enable_sni
        config[:FailureThreshold] = failure_threshold
        config[:FullyQualifiedDomainName] = target_domain if set_target_domain?
        config[:IPAddress] = target_ip if set_target_ip?
        config[:MeasureLatency] = measure_latency
        config[:Port] = target_port if set_target_port?
        config[:ResourcePath] = target_path if set_target_path?
        config[:RequestInterval] = interval
        config[:SearchString] = target_string if set_target_string?
      end

      props
    end

    def sanitized_name
      name.gsub('*', 'wildcard')
    end
  end
end
