# frozen_string_literal: true

require 'active_support/concern'

module Brevio::Session
  module WithSessionLogin
    extend ActiveSupport::Concern

    # By including this module into a Rails controller you will have access to the convenience methods
    # fetch_brevio_session(!) and brevio_logged_in? which acts as a wrapper around the decrypting of
    # predefined session cookies and sessions stored in Redis.
    #
    # We also require access to instance attributes like #request and the cookie jar, which are only
    # available in the context of a controller.
    #
    included do
      unless self <= ActionController::Base || instance_methods.include?(:session)
        raise 'Included Brevio::Session outside of controller'
      end
    end

    # Fetches the Brevio session from Redis, based on the encrypted key stored in the client's cookie.
    # Returns *nil* if there's no session present.
    #
    def fetch_brevio_session
      brevio_session, redis_key = fetch_session
      return nil if brevio_session.nil?
      if brevio_config.debug?
        brevio_config.logger.info "[brevio-session] Found session #{brevio_session.inspect}"
      end
      refresh_session(redis_key) unless params.transform_keys(&:underscore)[:no_session].present?
      brevio_session
    rescue RuntimeError => e
      brevio_config.logger.error "[brevio-session] #{e.message}"
      nil
    end

    # Calls the above function, but raises an exception if the session isn't present.
    def fetch_brevio_session!
      brevio_session, redis_key = fetch_session
      raise NilSession if brevio_session.nil?
      refresh_session(redis_key) unless params.transform_keys(&:underscore)[:no_session].present?
      brevio_session
    end

    # Returns a boolean flag indicating whether the current client has a Brevio session cookie set,
    # and whether this cookie contains a user ID.
    #
    def brevio_logged_in?
      brevio_session, = fetch_session
      brevio_session&.dig(:user_id).present?
    end

    private

    def brevio_config
      Config.config
    end

    def fetch_session
      brevio_config.logger.info '[brevio-session] Fetching Brevio session'
      cookie = request.cookie_jar[brevio_config.session_cookie]
      redis_key = Cookies::Parse.perform!(cookie)
      brevio_session = Redis.get(redis_key)
      raise NilSession if brevio_session.nil?
      [brevio_session.with_indifferent_access, redis_key]
    rescue RuntimeError => e
      brevio_config.logger.error "[brevio-session] --- 💣 Couldn't fetch Brevio session 💣 ---"
      brevio_config.logger.error "[brevio-session] #{e.message}"
      nil
    end

    # Refreshes the Brevio session cookie, avoding its expiry. This is helpful to
    # ensure the user stays logged in through interacting with the application.
    #
    def refresh_session(redis_key)
      brevio_config.logger.info '[brevio-session] Refreshing Brevio session' if brevio_config.debug?
      cookies[brevio_config.session_cookie] = {
        value: request.cookie_jar[brevio_config.session_cookie],
        domain: :all,
        expires: brevio_config.session_expire,
        httponly: true,
        secure: true
      }
      Redis.expire(redis_key, brevio_config.session_expire)
    rescue RuntimeError => e
      brevio_config.logger.error "[brevio-session] --- 💣 Couldn't refresh Brevio session 💣 ---"
      brevio_config.logger.error "[brevio-session] #{e.message}"
    end

  end
end