module Softwear
  module Lib
    module ControllerAuthentication
      extend ActiveSupport::Concern

      class NotSignedInError < StandardError
      end

      included do
        rescue_from NotSignedInError, with: :user_not_signed_in
        rescue_from Softwear::Auth::Model::AuthServerDown, with: :auth_server_down

        helper_method :current_user
        helper_method :user_signed_in?

        helper_method :destroy_user_session_path
        helper_method :users_path
        helper_method :user_path
        helper_method :edit_user_path
      end

      def user_class
        if Softwear::Auth::Model.descendants.size > 1
          raise "More than one descendent of Softwear::Auth::Model is not supported."
        elsif Softwear::Auth::Model.descendants.size == 0
          raise "Please define a user model that extends Softwear::Auth::Model."
        end
        Softwear::Auth::Model.descendants.first
      end

      # ====================
      # Action called when a NotSignedInError is raised.
      # ====================
      def user_not_signed_in
        redirect_to softwear_hub_url + "/users/sign_in?#{{return_to: request.original_url}.to_param}"
      end

      # ====================
      # Action called when a NotSignedInError is raised.
      # ====================
      def auth_server_down(error)
        respond_to do |format|
          format.html do
            render inline: \
              "<div class='panel panel-danger'>"\
                "<div class='panel-heading'>"\
                  "<h3 class='panel-title'>#{error.message}</h3>"\
                "</div>"\
                "<div class='panel-body'>"\
                  "Not all site functions will work until the problem is resolved. "\
                  "<a href='javascripr' onclick='history.go(-1);return false;' class='btn btn-default'>Go back.</a>"\
                "</div>"\
              "</div>"
          end

          format.js do
            render inline: "alert(\"#{error.message.gsub('"', '\"')}\");"
          end
        end
      end

      # ====================
      # Drop this into a before_filter to require a user be signed in on every request -
      # just like in Devise.
      # ====================
      def authenticate_user!
        if user_token.blank?
          raise NotSignedInError, "No token"
        end

        if user = user_class.auth(user_token)
          @current_user = user
        else
          self.user_token = nil
          raise NotSignedInError, "Invalid token"
        end
      end

      def current_user
        return @current_user if @current_user

        if @current_user.nil? && !user_token.blank?
          @current_user = user_class.auth(user_token)
        else
          nil
        end
      end

      def user_signed_in?
        !!current_user
      end

      # -- url uelpers --

      def softwear_hub_url
        if Rails.env.production?
          Figaro.env.softwear_hub_url || (raise "Please set softwear_hub_url in application.yml")
        elsif Rails.env.test?
          'http://hub.example.com'
        else
          Figaro.env.softwear_hub_url || 'http://localhost:2995'
        end
      end

      def destroy_user_session_path
        softwear_hub_url + "/users/sign_out"
      end

      def user_path(user)
        user_id = user.is_a?(user_class) ? user.id : user
        softwear_hub_url + "/users/#{user_id}"
      end

      def edit_user_path(user)
        user_id = user.is_a?(user_class) ? user.id : user
        softwear_hub_url + "/users/#{user_id}/edit"
      end

      def users_path
        softwear_hub_url + "/users"
      end

      private

      def user_token
        session[:user_token]
      end

      def user_token=(new_token)
        session[:user_token] = new_token
      end
    end
  end
end