# frozen_string_literal: true

require 'omniauth/strategies/oauth2'

module OmniAuth
  module Strategies
    # Authenticate to Vkontakte utilizing OAuth 2.0 and retrieve
    # basic user information.
    # documentation available here:
    # http://vk.com/dev/authentication
    #
    # @example Basic Usage
    #     use OmniAuth::Strategies::Vkontakte, 'API Key', 'Secret Key'
    #
    class Vkontakte < OmniAuth::Strategies::OAuth2
      class NoRawData < StandardError; end

      API_VERSION = '5.107'

      DEFAULT_SCOPE = ''

      option :name, 'vkontakte'

      option :client_options, {
        site: 'https://api.vk.com/',
        token_url: 'https://oauth.vk.com/access_token',
        authorize_url: 'https://oauth.vk.com/authorize',
        auth_scheme: :request_body
      }

      option :authorize_options, %i[scope display]

      option :redirect_url, nil

      uid { raw_info['id'].to_s }

      # https://github.com/intridea/omniauth/wiki/Auth-Hash-Schema
      info do
        {
          name: "#{raw_info['first_name']} #{raw_info['last_name']}".strip,
          nickname: raw_info['nickname'],
          email: access_token.params['email'],
          first_name: raw_info['first_name'],
          last_name: raw_info['last_name'],
          image: image_url,
          location: location,
          urls: {
            'Vkontakte' => "http://vk.com/#{raw_info['screen_name']}"
          }
        }
      end

      extra do
        {
          'raw_info' => raw_info
        }
      end

      def raw_info
        access_token.options[:mode] = :query
        access_token.options[:param_name] = :access_token
        @raw_info ||= begin
          result = access_token.get('/method/users.get', params: params).parsed['response']

          raise NoRawData, result unless result.is_a?(Array) && result.first

          result.first
        end
      end

      # You can pass +display+, +revoke+ or +scope+ params to the auth request,
      # if you need to set them dynamically.
      #
      # http://vk.com/dev/oauth_dialog
      #
      # +revoke+ revokes access and re-authorizes user.
      def authorize_params
        super.tap do |params|
          # just a copypaste from ominauth-facebook
          %w[display state scope revoke].each do |v|
            next unless request.params[v]

            params[v.to_sym] = request.params[v]

            # to support omniauth-oauth2's auto csrf protection
            session['omniauth.state'] = params[:state] if v == 'state'
          end

          params[:scope] ||= DEFAULT_SCOPE
        end
      end

      private

      def params
        {
          fields: info_options,
          lang: lang_option,
          https: https_option,
          v: API_VERSION
        }
      end

      def callback_url
        options.redirect_url || (full_host + script_name + callback_path)
      end

      def info_options
        # http://vk.com/dev/fields
        fields = %w[
          nickname screen_name sex city country online bdate
          photo_50 photo_100 photo_200 photo_200_orig photo_400_orig
        ]
        fields.concat(options[:info_fields].split(',')) if options[:info_fields]
        fields.join(',')
      end

      def lang_option
        options[:lang] || ''
      end

      def https_option
        options[:https] || 0
      end

      def image_url
        case options[:image_size]
        when 'mini'
          raw_info['photo_50']
        when 'bigger'
          raw_info['photo_100']
        when 'bigger_x2'
          raw_info['photo_200']
        when 'original'
          raw_info['photo_200_orig']
        when 'original_x2'
          raw_info['photo_400_orig']
        else
          raw_info['photo_50']
        end
      end

      def location
        country = raw_info.fetch('country', {})['title']
        city = raw_info.fetch('city', {})['title']
        @location ||= [country, city].compact.join(', ')
      end

      def callback_phase
        super
      rescue NoRawData => e
        fail!(:no_raw_data, e)
      end
    end
  end
end
