require_relative "sdk"

module Clerk
  class RackMiddleware
    def initialize(app)
      @app = app
    end

    def call(env)
      req = Rack::Request.new(env)
      token = req.cookies["__session"]

      if token
        sess_id = req.params["_clerk_session_id"]
        begin
          env["clerk_session"] = fetch_session(token, sess_id)
        rescue Errors::Authentication => e
          env["clerk_error"] = e
        end
      end
      if sess = env["clerk_session"]
        env["clerk_user"] = fetch_user(sess["user_id"])
      end
      @app.call(env)
    end

    private
    def clerk_sdk
      SDK.new
    end

    def fetch_session(token, sess_id)
      cache_key = token.split(".")[1]
      if sess_id
        session = cached_fetch("clerk_session:#{sess_id}:#{cache_key}") do
          sdk = clerk_sdk
          sdk.sessions.verify_token(sess_id, token)
        end
      else
        session = cached_fetch("clerk_session:#{cache_key}") do
          sdk = clerk_sdk
          client = sdk.clients.verify_token(token)
          sess_id = client["last_active_session_id"]
          client["sessions"].find do |sess|
            sess["id"] == sess_id
          end
        end
      end
    end

    def fetch_user(user_id)
      sdk = clerk_sdk
      cached_fetch("clerk_user:#{user_id}") do
        sdk.users.find(user_id)
      end
    end

    def cached_fetch(key, &block)
      if store = Clerk.configuration.middleware_cache_store
        store.fetch(key, expires_in: cache_ttl, &block)
      else
        yield
      end
    end

    def cache_ttl
      60
    end
  end
end