# frozen_string_literal: true

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

module Litany
  class ElastiCacheReplicationGroup < Resource
    include Stack
    include Taggable

    cfn_type 'AWS::ElastiCache::ReplicationGroup'
    parent_resource :service

    child_resource ElastiCacheSubnetGroup, :subnet_group

    property :automatic_failover, true, [false, true]
    property :auto_minor_version_upgrade, true, [false, true]
    property :description, nil, [String, nil]
    property :engine, 'redis', ['redis']
    property :node_type, 'cache.m4.large', String # TODO: Replace with a proper enum
    property :port, 6379, [6379]
    property :replicas, 1, [0..5]
    property :shards, 1, [1..10]
    property :version, '3.2.4', [String]

    resource_collection CloudWatchAlarm, :cw_alarm, required: false
    resource_collection DNSRecord, :dns_record, required: false
    resource_collection SecurityGroup, :security_group

    resource_reference VPC, :vpc, inherited: true

    resource_references Environment, :only_in, required: false

    def alarm(alarm_name, &block)
      deferred do
        (1..(1+replicas)).each do |index|
          raise 'The alarm helper expects a max replica count of 9.' if index > 9

          alarm = cw_alarm(:"#{name}_elasticache_node_#{index}_#{alarm_name}", &block)
          dimension = { Name: :CacheClusterId, Value: join([ref(self), "00#{index}"], '-') }
          alarm.dimension(dimension)
        end
      end
    end

    def replication_group_id
      @replication_group_id ||= "#{name[0..9]}-#{active_environment.short_region}-#{Digest::SHA256.hexdigest(output_name)}".tr('_', '-')[0..19]
    end

    def add_alias(name, zone)
      record = dns_record(name)
      record.type :cname
      record.target ref(self, 'PrimaryEndPoint.Address')
      record.zone(zone)
      record
    end

    def add_regional_alias(subdomain:, domain:, zone: nil)
      add_alias "#{subdomain}-#{active_environment_name}.#{domain}", zone || domain
    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_record(project.alias_compat_mode? ? record_name : resource_name)
      record.type(:cname)
      record.record_name record_name
      record.zone zone || domain
      record.target(ref(self, 'PrimaryEndPoint.Address'))

      deferred { record.latency_routing if (type.nil? ? vpc.environments.length > 1 : type == :latency) }
    end

    def allow(ecs: nil, sg: nil, source: nil)
      raise 'Must specify a valid resource. Currently supported: ecs, sg' if ecs.nil? && sg.nil? && source.nil?

      deferred { reference_sg.ingress port: port, source: ECSService.new(ecs).reference_sg } unless ecs.nil?
      reference_sg.ingress(port: port, source: sg) unless sg.nil?
      reference_sg.ingress(port: port, source: source) unless source.nil?
    end

    def finalize_resource
      reference_sg.description "Allows for access to the #{name} elasticache service."
    end

    def properties
      # Not implementing: CacheParameterGroupName, CacheSecurityGroupNames, NodeGroupConfiguration, NotificationTopicArn, NumCacheClusters, PreferredCacheClusterAZs, PreferredMaintenanceWindow, PrimaryClusterId, SnapshotArns, SnapshotName, SnapshotRetentionLimit, SnapshottingClusterId, SnapshotWindow

      props = {
        AutomaticFailoverEnabled: automatic_failover,
        AutoMinorVersionUpgrade: auto_minor_version_upgrade,
        CacheNodeType: node_type,
        CacheSubnetGroupName: ref(subnet_group),
        Engine: engine,
        EngineVersion: version,
        NumNodeGroups: shards,
        Port: port,
        ReplicasPerNodeGroup: replicas,
        ReplicationGroupDescription: description || "ElastiCache replication group named #{name}"
      }

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

      props
    end

    def reference_sg
      security_group :"#{name}_reference_sg"
    end

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