# frozen_string_literal: true

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

require 'litany/meta_resources/rds'

module Litany
  class RDSAurora < RDS
    include Stack

    child_resource RDSCluster, :cluster
    child_resource RDSClusterParameterGroup, :cluster_parameters, automatic: false

    property :binlogs, nil, [nil, :row, :statement, :mixed]
    property :cluster_size, 2, 1..16
    property :engine, :aurora, [:aurora, :aurora_mysql, :aurora_postgresql]
    property :port, 3306, 1150..65_535
    property :source, nil, [nil, String]
    property :version, nil do |value|
      valid_versions = {
        aurora: [
          '5.6.10a',
          '5.6.10a',
          '5.6.10a',
          '5.6.10a',
          '5.6.10a',
          '5.6.mysql_aurora.1.17.9',
          '5.6.mysql_aurora.1.19.0',
          '5.6.mysql_aurora.1.19.0',
          '5.6.mysql_aurora.1.19.1',
          '5.6.mysql_aurora.1.19.2',
          '5.6.mysql_aurora.1.19.2',
          '5.6.mysql_aurora.1.19.5',
          '5.6.mysql_aurora.1.19.5',
          '5.6.mysql_aurora.1.19.6',
          '5.6.mysql_aurora.1.19.6',
          '5.6.mysql_aurora.1.20.0',
          '5.6.mysql_aurora.1.20.1',
          '5.6.mysql_aurora.1.20.1',
          '5.6.mysql_aurora.1.21.0',
          '5.6.mysql_aurora.1.22.0',
          '5.6.mysql_aurora.1.22.1',
          '5.6.mysql_aurora.1.22.1.3',
          '5.6.mysql_aurora.1.22.2',
          '5.6.mysql_aurora.1.22.2',
          '5.6.mysql_aurora.1.23.0'
        ],
        aurora_mysql: [
          '5.7.12',
          '5.7.mysql_aurora.2.03.2',
          '5.7.mysql_aurora.2.03.3',
          '5.7.mysql_aurora.2.03.4',
          '5.7.mysql_aurora.2.04.0',
          '5.7.mysql_aurora.2.04.1',
          '5.7.mysql_aurora.2.04.2',
          '5.7.mysql_aurora.2.04.3',
          '5.7.mysql_aurora.2.04.4',
          '5.7.mysql_aurora.2.04.5',
          '5.7.mysql_aurora.2.04.6',
          '5.7.mysql_aurora.2.04.7',
          '5.7.mysql_aurora.2.04.8',
          '5.7.mysql_aurora.2.04.9',
          '5.7.mysql_aurora.2.05.0',
          '5.7.mysql_aurora.2.06.0',
          '5.7.mysql_aurora.2.07.0',
          '5.7.mysql_aurora.2.07.1',
          '5.7.mysql_aurora.2.07.1',
          '5.7.mysql_aurora.2.07.2',
          '5.7.mysql_aurora.2.08.0',
          '5.7.mysql_aurora.2.08.1',
          '5.7.mysql_aurora.2.08.2',
          '5.7.mysql_aurora.2.09.0'
        ],
        aurora_postgresql: [
          '9.6.3',
          '9.6.6',
          '9.6.8',
          '9.6.9',
          '9.6.11',
          '9.6.12',
          '9.6.16',
          '9.6.17',
          '9.6.18',
          '10.4',
          '10.5',
          '10.6',
          '10.7',
          '10.7',
          '10.11',
          '10.12',
          '10.13',
          '11.4',
          '11.6',
          '11.7',
          '11.8'
        ]
      }

      # thought: this could be improved with some pattern matching

      raise "Invalid db version #{engine} #{value}. Must be one of #{valid_versions[engine]}" unless valid_versions[engine].include?(value)
    end

    resource_collection DNSRecord, :dns_record, required: false

    resource_reference Environment, :source_region, required: false

    resource_references Environment, :only_in, required: false

    validator :replication do
      raise 'You must provide a `source` if you set `source_region`.' if set_source_region? && !set_source?
      raise 'You must set `binlogs` when enabling replication via `source`.' if set_source? && (!set_binlogs? || binlogs.nil?)
    end

    def add_alias(name, zone, endpoint: :primary)
      raise 'You may only set the `endpoint` to `:primary` or `:read`.' unless [:primary, :read].include?(endpoint)

      attr = 'Endpoint.Address' if endpoint == :primary
      attr = 'ReadEndpoint.Address' if endpoint == :read

      record = dns_record(name)
      record.type :cname
      record.target ref(cluster, attr)
      record.zone(zone)
      record
    end

    def add_dns_alias(subdomain:, domain:, zone: nil, type: nil, endpoint: :primary)
      raise 'You may only set the `endpoint` to `:primary` or `:read`.' unless [:primary, :read].include?(endpoint)
      raise 'If type is provided to `add_dns_alias` it must be must be either `:normal`, `:regional`.' unless [nil, :normal, :regional].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.record_name record_name
      record.zone zone || domain
      record.type :cname
      record.target ref(cluster, endpoint == :primary ? 'Endpoint.Address' : 'ReadEndpoint.Address')
    end

    def finalize_resource
      super

      cluster_parameters.parameter(:binlog_format, binlogs.upcase) if set_binlogs?

      child_resources << cluster_parameters if set_cluster_parameters? && cluster_parameters.parameters.count.positive?
      cluster_size.times { |iteration| instance "#{name}_#{iteration.to_s.rjust(2, '0')}" } if instances.count.zero?
    end
  end
end
