# frozen_string_literal: true

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

module Litany
  class ApplicationLoadBalancerTargetGroup < Resource
    include Taggable

    cfn_type 'AWS::ElasticLoadBalancingV2::TargetGroup'
    parent_resource :load_balancer

    flag :disable_healthy_alarm, false
    flag :sticky_sessions, false, inherited: true

    property :deregistration_delay, 10, [5..3600], inherited: true
    property :health_check_interval, 5, [5..300], inherited: true
    property :health_check_path, '/', [String], inherited: true
    property :health_check_port, nil, [nil, Integer, String], inherited: true, &:to_s
    property :health_check_protocol, nil, [nil, :http, :https, :tcp, :HTTP, :HTTPS, :TCP], inherited: true, &:upcase
    property :health_check_timeout, 2, [2..60], inherited: true
    property :healthy_threshold, 3, [2..10], inherited: true
    property :healthy_statuses, '200', [String, Integer, Range, Array], inherited: true do |value|
      case value
        when Array
          raise 'Invalid value for `healthy_statuses` array. Only Integers and Ranges allowed.' unless value.all? { |v| v.is_a?(Range) || v.is_a?(Integer) }
          value.collect { |status| status.is_a?(Range) ? "#{status.first}-#{status.last}" : status.to_s }.join(',').to_s
        when Integer
          value.to_s
        when Range
          "#{value.first}-#{value.last}"
        else
          raise 'Invalid value for `healthy_statuses` string. Only comma separated Integers and Ranges allowed.' unless value.split(',').all? { |v| /(^\d+$)|(^\d+-\d+$)/.match(v) }
          value
      end
    end
    property :port, 80, [1..65_535]
    property :protocol, :HTTP, [:http, :https, :tcp, :HTTP, :HTTPS, :TCP], &:upcase
    property :target_type, nil, [nil, :instance, :ip]
    property :unhealthy_threshold, 2, [2..10], inherited: true

    resource_references Instance, :target, required: false

    resource_collection CloudWatchAlarm, :alarm, required: false

    validator :health_check do
      raise 'Health check timeout, must be smaller than health check interval.' unless health_check_interval > health_check_timeout
    end

    def healthy_alarm(&block)
      alarm("#{name}_healthy", &block)
    end

    def finalize_resource
      return if disable_healthy_alarm?

      healthy_alarm do
        metric 'HealthyHostCount'
        threshold 1 unless set_threshold?
        unit :count
      end
    end

    def properties
      # TODO: Implement TargetGroupAttributes
      # Not Implementing: Name

      props = {
        HealthCheckIntervalSeconds: health_check_interval,
        HealthCheckPath: health_check_path,
        HealthCheckProtocol: health_check_protocol || protocol,
        HealthCheckTimeoutSeconds: health_check_timeout,
        HealthyThresholdCount: healthy_threshold,
        Matcher: {
          HttpCode: healthy_statuses
        },
        Port: port,
        Protocol: protocol,
        TargetGroupAttributes: [
          {
            Key: 'deregistration_delay.timeout_seconds',
            Value: deregistration_delay.to_s
          }
        ],
        UnhealthyThresholdCount: unhealthy_threshold,
        VpcId: ref(load_balancer.service.vpc)
      }

      if sticky_sessions?
        props[:TargetGroupAttributes] += [
          {
            Key: 'stickiness.enabled',
            Value: true
          },
          {
            Key: 'stickiness.type',
            Value: :lb_cookie
          }
        ]
      end

      props[:TargetType] = target_type if set_target_type?
      props[:HealthCheckPort] = health_check_port unless health_check_port.nil?
      props[:Targets] = targets.collect { |target| {Id: ref(target), Port: port} } unless targets.empty?

      props
    end

    def default_tags
      {
          LitanyTargetGroupName: self.name,
      }
    end
  end
end
