# frozen_string_literal: true

module Api
  module V1
    class ApiController < ActionController::API
      ##### Doorkeeper
      include Doorkeeper::Rails::Helpers

      ##### Constants
      COLLECTION_PER_PAGE = 15
      SEARCH_PER_PAGE = 10

      ##### Errors
      rescue_from ActiveRecord::RecordInvalid, with: :render_unprocessable_entity_response
      rescue_from ActiveRecord::RecordNotDestroyed, with: :render_unprocessable_entity_response
      rescue_from ActiveRecord::RecordNotFound, with: :render_not_found_response_without_object
      rescue_from ActiveRecord::RecordNotSaved, with: :render_unprocessable_entity_response
      rescue_from ActiveRecord::RecordNotUnique, with: :render_unprocessable_entity_response
      rescue_from Api::V1::SameFavoriteUserError, with: :render_unprocessable_entity_response
      rescue_from Api::V1::FavoriteAlreadyExistsError, with: :render_unprocessable_entity_response
      rescue_from Api::V1::NoJobProvidedError, with: :render_unprocessable_entity_response
      rescue_from Api::V1::TooManySkillsOfTypeError, with: :render_unprocessable_entity_response
      rescue_from Api::V1::IncompatibleWeaponForPositionError, with: :render_unprocessable_entity_response
      rescue_from Api::V1::UnauthorizedError, with: :render_unauthorized_response
      rescue_from ActionController::ParameterMissing, with: :render_unprocessable_entity_response

      rescue_from GranblueError do |e|
        render_error(e)
      end

      ##### Hooks
      before_action :current_user
      before_action :default_content_type
      around_action :n_plus_one_detection, unless: -> { Rails.env.production? }

      ##### Responders
      respond_to :json

      ##### Methods
      # Returns the latest update
      def version
        render json: UpdateBlueprint.render_as_json(AppUpdate.last)
      end

      # Assign the current user if the Doorkeeper token isn't nil, then
      # update the current user's last seen datetime and last IP address
      # before returning
      def current_user
        @current_user ||= User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token

        @current_user
      end

      def admin_mode
        if current_user&.admin? && request.headers['X-Admin-Mode']
          @admin_mode ||= request.headers['X-Admin-Mode'] == 'true'
        end

        @admin_mode
      end

      def edit_key
        @edit_key ||= request.headers['X-Edit-Key'] if request.headers['X-Edit-Key']

        @edit_key
      end

      # Set the response content-type
      def content_type(content_type)
        response.headers['Content-Type'] = content_type
      end

      # Set the default response content-type to application/javascript
      # with a UTF-8 charset
      def default_content_type
        content_type('application/javascript; charset=utf-8')
      end

      ### Error response methods
      def render_error(error)
        if error
          render action: 'errors', json: error.to_hash, status: error.http_status
        else
          render action: 'errors'
        end
      end

      def render_unprocessable_entity_response(exception)
        render json: ErrorBlueprint.render_as_json(nil, errors: exception.to_hash),
               status: :unprocessable_entity
      end

      def render_validation_error_response(object)
        render json: ErrorBlueprint.render_as_json(nil, errors: object.errors),
               status: :unprocessable_entity
      end

      def render_not_found_response_without_object
        render json: ErrorBlueprint.render(nil,
                                           error: {
                                             message: 'Object could not be found',
                                             code: 'not_found'
                                           }), status: :not_found
      end

      def render_not_found_response(object)
        render json: ErrorBlueprint.render(nil, error: {
          message: "#{object.capitalize} could not be found",
          code: 'not_found'
        }), status: :not_found
      end

      def render_unauthorized_response
        render json: ErrorBlueprint.render_as_json(nil),
               status: :unauthorized
      end

      private

      def restrict_access
        raise UnauthorizedError unless current_user
      end

      def n_plus_one_detection
        Prosopite.scan
        yield
      ensure
        Prosopite.finish
      end
    end
  end
end
