# frozen_string_literal: true

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

module Litany
  class RDS < MetaResource
    include Stack
    include Taggable

    child_resource RDSInstanceOptionGroup, :options_group, automatic: :on_access
    child_resource RDSInstanceParameterGroup, :instance_parameters, automatic: :on_access
    child_resource RDSSubnetGroup, :subnet_group

    # @!group Properties

    flag :publicly_accessible, false

    # @!endgroup
    
    property :backup_retention, 7, [1..35] # Supports 0, but changing to/from 0 can be disruptive and means there's no backups.
    property :backup_window, nil, [nil, String] do |value|
      raise 'Backup window must be at least 30 minutes and formatted as hh24:mi-hh24:mi' unless value.nil? || /[0-9]{2}:[0-9]{2}-[0-9]{2}:[0-9]{2}/.match(value)
      value
    end
    property :encrypted, false, [true, false]
    property :instance_type, 'db.r3.large', [String] do |value|
      raise "Invalid instance type #{value}. Must match `db.[class].[size]`" unless /db\.(?:t|m|r)[1-5]\.(?:[2-8]|10)?(?:micro|small|medium|large|xlarge)/.match(value)
      value
    end
    property :maintenance_window, nil, [nil, String] do |value|
      raise 'Maintenance window must be at least 30 minutes and formatted as hh24:mi-hh24:mi' unless value.nil? || /(Mon|Tue|Wed|Thu|Fri|Sat|Sun):[0-9]{2}:[0-9]{2}-(Mon|Tue|Wed|Thu|Fri|Sat|Sun):[0-9]{2}:[0-9]{2}/.match(value)
      value
    end
    property :password, nil, [nil, String], secret: true
    property :port, nil, 1024..65_535
    property :username, nil, [nil, String]
    property :snapshot, nil, [nil, String]

    resource_collection RDSInstance, :instance
    resource_collection SecurityGroup, :security_group, required: false

    resource_reference KMSKey, :kms_key, required: false
    resource_reference VPC, :vpc, inherited: true

    def access_sg
      engine = self.class.name.sub('Litany::', '').sub('RDS', '').downcase
      engine = 'generic' if engine.empty?

      security_group(:"#{name}_#{engine}_rds_access")
    end

    def default_tags
      {
        LitanyRDSName: name,
        LitanyRDSType: self.class.name.demodulize.gsub(/^RDS/, '').downcase
      }
    end

    def finalize_resource
      access_sg.description "Access to the #{name} RDS Service"
    end

    def ingress(source: nil, beanstalk: nil, environment: nil, ecs: nil)
      configs = [source.nil?, beanstalk.nil?, ecs.nil?]

      raise 'You must provide either a `source`, ecs cluster, or beanstalk environment.' if configs.count(true) == 3
      raise 'You may only provide one `source`, ecs cluster, or a beanstalk environment in the same `ingress` call.' unless configs.count(false) == 1
      raise 'You must specify both `beanstalk` and `environment` to allow ingress from a beanstalk environment' if !beanstalk.nil? && environment.nil?

      access_sg.ingress port: port, source: Beanstalk.new(beanstalk).beanstalk_environment(environment).reference_sg unless beanstalk.nil?
      deferred { access_sg.ingress port: port, source: ECSService.new(ecs).reference_sg } unless ecs.nil?
      access_sg.ingress port: port, source: source unless source.nil?
    end
  end
end
