# frozen_string_literal: true

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

## Not yet suitable for general consumption.

module Litany
  class CloudFrontDistribution < Resource
    include Stack

    cfn_type 'AWS::CloudFront::Distribution'

    # Certificate
    # property :certificate_arn, nil, [String]
    flag :require_sni, false

    # Default Cache Behavior
    property :allow_methods, :read, [:read, :write, :basic_read]
    property :cache_methods, :read, [:read, :basic_read]
    property :certificate_arn, nil, [String, nil]
    property :compress_cache, true, [true, false]
    property :default_ttl, 86_400, Integer
    property :max_ttl, 31_536_000, Integer
    property :min_ttl, 0, Integer
    property :viewer_protocol, :redirect_to_https, [:allow_all, :https_only, :redirect_to_https]

    property_collection :forward_cookie, [], [String], required: false
    property_collection :forward_header, [], [String], required: false
    property_collection :query_string_cache_key, [], [String], required: false

    # Distribution
    property :comment, nil, [nil, String]
    property :default_root, nil, [nil, String]
    property :enabled, true, [true, false]
    property :http_version, :'http1.1', [:'http1.1', :http2]
    property :price_class, :all, [:all, :'200', :'100']

    property_collection :alias_name, [], [String], required: false

    # Logging
    flag :log_cookies, false
    property :access_logs_prefix, nil, [String, Symbol, nil]
    resource_reference S3Bucket, :access_logs_bucket, required: false


    # Origin
    property :origin_http_port, 80, 1..65_535
    property :origin_https_port, 443, 1..65_535
    property :origin_path, nil, [nil, String]
    property :origin_protocol_policy, :'match-viewer', [:'match-viewer', :'http-only', :'https-only']
    property_collection :origin_ssl_protocol, [:TLSv1], [:TLSv1, :'TLSv1.1', :'TLSv1.2', :'SSLv3']
    property :origin_url, nil, [nil, String], &:to_s

    resource_collection CloudFrontCacheBehavior, :path, required: false
    resource_collection DNSAlias, :dns_alias, required: false

    resource_reference Certificate, :certificate, required: false
    resource_reference Environment, :environment, inherited: true
    resource_reference S3Bucket, :origin_bucket, required: false

    validator(:alias_casing) do
      raise 'Your aliases must be all lowercase.' unless !set_alias_names? or alias_names.collect { |name| name == name.downcase }.all?
    end

    validator(:origin) do
      raise 'You must set `origin_url` or `origin_bucket`, but not both.' unless set_origin_url? ^ set_origin_bucket?
    end

    validator(:origin_path_form) do
      raise 'You must specify an origin_path that starts with a "/" and does not end with a "/".' if set_origin_path? && (origin_path[0] != '/' || origin_path[-1] == '/')
    end

    validator(:ensure_iad) do
      raise 'Redirects must have an environment with a region of `:us_east_1`.' unless environment.region == :'us-east-1'
    end

    def add_alias(name, zone)
      dns = dns_alias(name)
      dns.target(self)
      dns.zone(zone)
    end

    def method_set(set)
      case set
        when :read
          [:HEAD, :GET, :OPTIONS]
        when :basic_read
          [:HEAD, :GET]
        when :write
          [:HEAD, :GET, :OPTIONS, :DELETE, :PATCH, :POST, :PUT]
      end
    end

    def properties
      # TODO: Revisit or implement the following:
      ## Multi-Origin
      ## CacheBehaviors, CustomErrorResponses, Logging, WebACLId
      ## ViewerCertificate.IamCertificateId
      ## Origins.S3OriginConfig.OriginAccessIdentity Origins.OriginCustomHeaders

      # Not Implementing:
      ## Restrictions
      ## DefaultCacheBehavior. SmoothStreaming, TrustedSigners
      ## ViewerCertificate.CloudFrontDefaultCertificate

      config = {
        DefaultCacheBehavior: {
          AllowedMethods: method_set(allow_methods),
          CachedMethods: method_set(cache_methods),
          Compress: compress_cache,
          DefaultTTL: default_ttl,
          ForwardedValues: {
            QueryString: set_query_string_cache_keys?
          },
          MaxTTL: max_ttl,
          MinTTL: min_ttl,
          TargetOriginId: :default,
          ViewerProtocolPolicy: viewer_protocol.tr('_', '-')
        },
        Enabled: true,
        HttpVersion: http_version,
        Origins: [],
        PriceClass: "PriceClass_#{price_class.capitalize}",
        ViewerCertificate: {
          AcmCertificateArn: certificate_arn.nil? ? ref(certificate) : certificate_arn,
          MinimumProtocolVersion: :TLSv1,
          SslSupportMethod: require_sni? ? :'sni-only' : :vip
        }
      }

      config[:CacheBehaviors] = paths if set_paths?

      if set_access_logs_bucket?
        config[:Logging] = {
          Bucket: ref(access_logs_bucket, 'DomainName'),
          IncludeCookies: log_cookies?,
        }

        config[:Logging][:Prefix] = access_logs_prefix if set_access_logs_prefix?
      end

      if set_origin_bucket?
        config[:Origins] << {
          DomainName: ref(origin_bucket, :DomainName),
          Id: :default,
          S3OriginConfig: {}
        }
      end

      if set_origin_url?
        config[:Origins] << {
          DomainName: origin_url,
          Id: :default,
          CustomOriginConfig: {
            OriginProtocolPolicy: origin_protocol_policy
          },
        }

        config[:Origins][0][:CustomOriginConfig][:HTTPPort] = origin_http_port unless origin_protocol_policy == :'https-only'

        unless origin_protocol_policy == :'http-only'
          config[:Origins][0][:CustomOriginConfig][:HTTPSPort] = origin_https_port
          config[:Origins][0][:CustomOriginConfig][:OriginSSLProtocols] = origin_ssl_protocols
        end
      end

      config[:Aliases] = alias_names if set_alias_names?
      config[:Comment] = comment if set_comment?
      config[:DefaultRootObject] = default_root if set_default_root?

      config[:Origins][0][:OriginPath] = origin_path if set_origin_path?

      config[:DefaultCacheBehavior][:ForwardedValues][:Cookies] = { Forward: :whitelist, WhitelistedNames: forward_cookies } if set_forward_cookies?
      config[:DefaultCacheBehavior][:ForwardedValues][:Headers] = forward_headers if set_forward_headers?
      config[:DefaultCacheBehavior][:ForwardedValues][:QueryStringCacheKeys] = query_string_cache_keys if set_query_string_cache_keys?

      {
        DistributionConfig: config
      }
    end
  end
end
