# frozen_string_literal: true

require 'litany/resource'

module Litany
  class IAMRole < Resource
    cfn_type 'AWS::IAM::Role'

    child_resource IAMPolicyDocument, :sts_policy

    output 'Arn'

    property :path, '/', String
    property :role_name, nil, [nil, String]

    property_collection :managed_policy, [], String, required: false
    property_collection :trusted_principal, [], Hash, required: false

    resource_collection IAMPolicyDocument, :policy, required: false

    def allow(actions:, resource:, condition: nil)
      allowed_actions = (actions.is_a?(String) ? [actions] : actions)
      next_policy do
        next_statement do
          actions allowed_actions
          on_resource resource
          condition condition unless condition.nil?
        end
      end
    end

    def finalize_resource
      principals = self.trusted_principals # Scoping gotcha
      sts_policy.next_statement do
        actions 'sts:AssumeRole'
        principals.each { |p| allow_principal p }
      end
    end

    def next_policy(&block)
      @next_policy_id ||= 0
      policy format("#{resource_name.snake_case}_Policy%03d", @next_policy_id += 1).to_sym, &block
    end

    def secret_access(secret, region: { Ref: 'AWS::Region' }, key: nil)
      policy = next_policy


      statement = policy.next_statement
      statement.actions ['secretsmanager:GetSecretValue']
      statement.resource arn_builder(service: 'secretsmanager', region: region, resource: "secret:#{secret}")

      return if key.nil?

      if key.is_a?(String)
        statement = policy.next_statement
        statement.actions ['kms:Decrypt']
        statement.resource arn_builder(service: 'kms', region: region, resource: 'key', path: key)
      elsif !key.is_a?(Symbol) && key.is_a?(KMSKey)
        raise 'Key must be one of the following: nil, string, symbol, or KMSKey'
      end

      key = KMSKey.new(key) if key.is_a?(Symbol)
      key.allow(:read, role: self)
    end

    def trust(**kwargs)
      trusted_principal kwargs
    end

    def properties
      props = {
        AssumeRolePolicyDocument: sts_policy,
        Path: path,
        RoleName: role_name || default_role_name
      }

      props[:ManagedPolicyArns] = managed_policies if set_managed_policies?
      props[:Policies] = policies.map { |p| { PolicyName: p.name.pascal_case, PolicyDocument: p } } unless policies.empty?

      props
    end

    def default_role_name
      "#{resource_name}-#{active_environment.pretty_region}-#{Digest::SHA256.hexdigest(output_name)}".tr('_', '-')[0..62]
    end
  end
end
