# frozen_string_literal: true

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

module Litany
  class IAMPolicyStatement < MetaResource
    # TODO: implement conditions

    property :actions, nil, [String, Array] # TODO: change to enum, once we have enums
    property :effect, :allow, [:allow, :deny]

    property_collection :principal, [], required: false
    property_collection :resource, [], required: false
    property_collection :condition, [], required: false

    alias_method :on_resource, :resource # couldn't help being a little bit stylistic on this one

    def allow_principal(account: { Ref: 'AWS::AccountId' }, role: nil, service: nil, user: nil, beanstalk: nil, environment: nil, mssql: nil, ecs: nil, ecs_task: nil, redshift: nil)
      principal principal_builder(account, role, service, user, beanstalk, environment, mssql, ecs, ecs_task, redshift)
    end

    def merged_principals
      merged = {}

      principals.each do |principal|
        key, data = principal.first
        case merged[key]
          when NilClass
            merged[key] = data
          when Array
            merged[key] << data
          else
            merged[key] = [merged[key], data]
        end
      end

      merged
    end

    def principal_builder(account = { Ref: 'AWS::AccountId' }, role = nil, service = nil, user = nil, beanstalk = nil, environment = nil, mssql = nil, ecs = nil, ecs_task = nil, redshift = nil)
      raise "You may only pass in a role, service, or user; multiples are not allowed.  Received role: #{role.inspect}, service: #{service.inspect}, user: #{user.inspect}." if [role, service, user].count(nil) < 2
      raise 'You must specify both `beanstalk` and `environment` to allow access from a beanstalk environment' if beanstalk.nil? ^ environment.nil?
      raise 'You may not provide both a `role` and a `mssql` value.' if !role.nil? && !mssql.nil?
      raise 'You may not provide both a `role` and a `ecs` or `ecs_task` value.' if !role.nil? && (!ecs.nil? || !ecs_task.nil?)
      raise 'You may not provide both a `role` and a `redshift` value.' if !role.nil? && !redshift.nil?

      if service.nil? && beanstalk.nil? && mssql.nil? && ecs.nil? && ecs_task.nil? && redshift.nil?
        resource = 'root' if role.nil? && user.nil?
        resource = "role/#{role}" unless role.nil?
        resource = "user/#{user}" unless user.nil?

        { AWS: arn_builder(service: 'iam', account: account, region: '', resource: resource) }
      elsif service.nil?
        role = Beanstalk.new(beanstalk).beanstalk_environment(environment).instance_profile.role unless beanstalk.nil?
        role = RDSMssql.new(mssql).native_backup_role unless mssql.nil?
        role = ECSService.new(ecs).task_role unless ecs.nil?
        role = ECSScheduledTask.new(ecs_task).task_role unless ecs_task.nil?
        role = Redshift.new(redshift).permissions unless redshift.nil?

        { AWS: ref(role, 'Arn') }
      else
        { Service: service }
      end
    end

    def statement
      # Not implementing at this time: Federated Principals, NotPrincipal, NotAction, NotResource

      clause = {
        Sid: name.pascal_case,
        Effect: effect.capitalize,
        Action: actions
      }

      clause[:Resource] = resources if set_resources?
      clause[:Principal] = merged_principals if set_principals?
      clause[:Condition] = conditions.reduce({}, :deep_merge) if set_conditions?

      clause
    end
  end
end
