module Twitch
  ACCEPTED_ADMIN_EMAIL_DOMAINS = ['twitch.tv', 'justin.tv']
  ACCEPTED_SUB_ADMIN_EMAIL_DOMAINS = ['mods.twitch.tv', 'mods.justin.tv']
  ACCEPTED_EXTERNAL_EMAIL_DOMAINS = ['amazon.com']

  ACCEPTED_EMAIL_DOMAINS =
    ACCEPTED_SUB_ADMIN_EMAIL_DOMAINS +
    ACCEPTED_EXTERNAL_EMAIL_DOMAINS +
    ACCEPTED_ADMIN_EMAIL_DOMAINS

  module UpdateRoles
    class InvalidEmailError < StandardError; end
    class InvalidAdminEmailError < StandardError; end
    class InvalidSubAdminEmailError < StandardError; end
    class InvalidGlobalModEmailError < StandardError; end
    class UnverifiedEmailError < StandardError; end
    class TwoFactorRequiredError < StandardError; end
    class UserServiceError < StandardError; end
  end

  class Roles < Twitch::Base
    class << self
      def update(user_id, roles)
        user = User.find(user_id)

        if roles.any? { |_, is_assigning_role| is_assigning_role }
          raise UpdateRoles::InvalidEmailError unless acceptable_domain(user)
          raise UpdateRoles::InvalidAdminEmailError if roles[:admin] && invalid_admin_grant?(roles, user)
          raise UpdateRoles::InvalidSubAdminEmailError if roles[:subadmin] && invalid_sub_admin_grant?(roles, user)
          raise UpdateRoles::InvalidGlobalModEmailError if roles[:global_mod] && invalid_global_mod_grant?(roles, user)
          raise UpdateRoles::UnverifiedEmailError unless user.email_verified
          raise UpdateRoles::TwoFactorRequiredError unless user.two_factor_enabled?
        end
        response = patch("/users/#{user_id}/roles", body: roles.to_json)
        raise UpdateRoles::UserServiceError unless response.success?
      end

      private

      def invalid_admin_grant?(roles, user)
        roles[:admin] && !acceptable_admin_domain(user)
      end

      def invalid_sub_admin_grant?(roles, user)
        roles[:subadmin] && !acceptable_sub_admin_domain(user)
      end

      def invalid_global_mod_grant?(roles, user)
        roles[:global_mod] && !acceptable_global_mod_domain(user)
      end

      def acceptable_domain(user)
        ACCEPTED_EMAIL_DOMAINS.include?(email_domain(user))
      end

      def acceptable_admin_domain(user)
        ACCEPTED_ADMIN_EMAIL_DOMAINS.include?(email_domain(user))
      end

      def acceptable_sub_admin_domain(user)
        acceptable_admin_domain(user) ||
          ACCEPTED_SUB_ADMIN_EMAIL_DOMAINS.include?(email_domain(user))
      end

      def acceptable_global_mod_domain(user)
        acceptable_sub_admin_domain(user) ||
          ACCEPTED_EXTERNAL_EMAIL_DOMAINS.include?(email_domain(user))
      end

      def email_domain(user)
        user.email.split('@').last
      end
    end
  end
end
