# frozen_string_literal: true

# name: discourse-lti
# about: Allows login integration with a number of learning management systems via the LTI 1.3 standard.
# meta_topic_id: 196174
# version: 1.0
# authors: David Taylor
# url: https://github.com/discourse/discourse/tree/main/plugins/discourse-lti

enabled_site_setting :lti_enabled

register_svg_icon "graduation-cap"

module ::DiscourseLti
  PLUGIN_NAME = "discourse-lti"
  CUSTOM_DATA_CLAIM = "https://purl.imsglobal.org/spec/lti/claim/custom"
  DISCOURSE_INVITE_KEYS = %w[discourse_invite_link custom_discourse_invite_link] # Coursera prefixes keys with `custom_`

  class ShouldReconnect < StandardError
  end
end

after_initialize do
  # Check for invite URL in LTI custom fields
  # If present, and this is a new user, redirect the user to the invite url.
  # Otherwise, continue as normal
  on(:after_auth) do |authenticator, auth_result, session, cookies, request|
    next if !(authenticator.name.to_sym == :lti)
    next if auth_result.user # Only redirect new users to invite

    uaa =
      UserAssociatedAccount.find_by(
        provider_name: "lti",
        provider_uid: auth_result.extra_data[:uid],
      )
    next if uaa.nil?

    custom_data = uaa.extra.dig("raw_info", DiscourseLti::CUSTOM_DATA_CLAIM)
    next if custom_data.nil?

    invite = nil
    DiscourseLti::DISCOURSE_INVITE_KEYS.each { |k| break if invite = custom_data[k] }
    next if invite.nil?

    parsed =
      begin
        URI.parse(invite)
      rescue URI::Error
        next
      end
    next if parsed.nil?
    next if parsed.host && parsed.host != Discourse.current_hostname

    route = Discourse.route_for(parsed.path)
    next if !(route[:controller] == "invites" && route[:action] == "show")

    request.server_session[:destination_url] = parsed.to_s
  end

  on(:after_auth) do |authenticator, auth_result, session, cookies|
    next if !(authenticator.name.to_sym == :lti)
    if !auth_result.user && cookies["_t"] # User (probably) already logged in
      raise DiscourseLti::ShouldReconnect.new
    end
  end

  reloadable_patch do
    ::Users::OmniauthCallbacksController.rescue_from(DiscourseLti::ShouldReconnect) do
      session[:auth_reconnect] = true
      complete
    end
  end
end

require_relative "lib/discourse_lti/lti_omniauth_strategy"
require_relative "lib/discourse_lti/lti_authenticator"

auth_provider authenticator: DiscourseLti::LtiAuthenticator.new
