# frozen_string_literal: true

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

module Litany
  class ApplicationLoadBalancer < Resource
    include Taggable

    cfn_type 'AWS::ElasticLoadBalancingV2::LoadBalancer'
    parent_resource :service

    flag :dualstack, false, inherited: true
    flag :internal, false, inherited: true
    flag :sticky_sessions, false, inherited: true

    # TODO: validate access logs bucket and prefix if access_logs_enabled
    property :access_logs_enabled, false, [true, false]
    property :access_logs_prefix, nil, [String, Symbol, nil]
    property :deletion_protection, false, [true, false]
    property :idle_timeout, 60, [1..3600], inherited: true

    property_collection :valid_hostname, [], [String], required: false, inherited: true

    resource_collection ApplicationLoadBalancerListener, :listener
    resource_collection ApplicationLoadBalancerTargetGroup, :target_group
    resource_collection CloudWatchAlarm, :cloudwatch_alarm, required: false
    resource_collection DNSAlias, :dns_alias, required: false

    resource_reference S3Bucket, :access_logs_bucket, required: false
    resource_references SecurityGroup, :security_group

    def finalize_resource
      return unless set_valid_hostnames?

      listeners.each do |listener|
        good_target_group = listener.target_group
        listener.target_group = nil

        valid_hostnames.to_set.each_with_index do |hostname, index|
          rule_number = index + 1

          rule = listener.rule(:"#{listener.name}_hostname_routing_#{rule_number}")
          rule.host(hostname)
          rule.target_group(good_target_group)
          rule.priority(rule_number) # TODO: This rule needs to be first, but you can't have duplicate priority values.
        end

        response = listener.fixed_response
        response.status_code = 400
        response.content_type = 'text/plain'
        response.body = 'Bad Request'
      end
    end

    def add_alias(name, zone)
      domain_name = name == :'@' ? zone : "#{name}.#{zone}"

      record = dns_alias(domain_name)
      record.target(self)
      record.zone(zone)

      valid_hostname(domain_name.to_s)
    end

    # New hotness
    def add_dns_alias(subdomain:, domain:, zone: nil, type: nil)
      raise 'If type is provided to `add_dns_alias` it must be must be either `:normal`, `:regional`, or `:latency`.' unless [nil, :normal, :regional, :latency].include?(type)
      raise "Subdomain must either be a string or '@', it may not be nil." if subdomain.nil?

      subdomain = subdomain == '@' ? active_environment_name : "#{subdomain}-#{active_environment_name}" if type == :regional
      record_name = subdomain == '@' ? domain : "#{subdomain}.#{domain}"
      resource_name = zone.nil? ? record_name : :"#{record_name}_in_#{zone}"

      record = dns_alias(project.alias_compat_mode? ? record_name : resource_name)
      record.alias_name record_name
      record.zone zone || domain
      record.target(self)

      deferred do
        record.latency_routing if (type.nil? ? service.vpc.environments.length > 1 : type == :latency)
        valid_hostname(record_name.to_s)
      end
    end

    def add_listener(name, port:, target_port: nil, source_protocol: :https, target_protocol: :http, health_check_path: nil)
      listener(name).port port
      listener(name).protocol source_protocol
      listener(name).target_group :"#{name}"

      target_group(name).port target_port || port
      target_group(name).protocol target_protocol
      target_group(name).health_check_path health_check_path unless health_check_path.nil?

      return listener(name), target_group(name)
    end

    def access_logs(bucket:, prefix: nil)
      access_logs_enabled true
      access_logs_bucket bucket
      access_logs_prefix prefix || resource_name
    end

    def prevent_delete
      deletion_protection true
    end

    def properties
      # Not Implementing: Name

      props = {
        Scheme: internal? ? :internal : :'internet-facing',
        Subnets: ref(service.vpc.subnets),
        IpAddressType: dualstack? ? :dualstack : :ipv4
      }

      props[:SecurityGroups] = ref(security_groups) unless security_groups.empty?

      props[:LoadBalancerAttributes] = []
      if access_logs_enabled
        props[:LoadBalancerAttributes].push({ Key: 'access_logs.s3.enabled', Value: 'true' })
        props[:LoadBalancerAttributes].push({ Key: 'access_logs.s3.bucket', Value: ref(access_logs_bucket) })
        props[:LoadBalancerAttributes].push({ Key: 'access_logs.s3.prefix', Value: access_logs_prefix }) if set_access_logs_prefix?
      end
      props[:LoadBalancerAttributes].push({ Key: 'deletion_protection.enabled', Value: 'true' }) if deletion_protection
      props[:LoadBalancerAttributes].push({ Key: 'idle_timeout.timeout_seconds', Value: idle_timeout })

      props
    end

    def default_tags
      {
        LitanyALBName: self.name
      }
    end
  end
end
