# frozen_string_literal: true
require "excon"

class CustomWizard::Api::Authorization
  include ActiveModel::SerializerSupport

  attr_accessor :api_name,
                :auth_type,
                :auth_url,
                :token_url,
                :client_id,
                :client_secret,
                :auth_params,
                :access_token,
                :refresh_token,
                :token_expires_at,
                :token_refresh_at,
                :code,
                :username,
                :password

  def initialize(api_name, data = {})
    @api_name = api_name

    data.each { |k, v| self.send "#{k}=", v if self.respond_to?(k) }
  end

  def authorized
    @authorized ||= @access_token && @token_expires_at.to_datetime > Time.now
  end

  def self.set(api_name, new_data = {})
    api_name = api_name.underscore

    data = self.get(api_name, data_only: true) || {}

    new_data.each { |k, v| data[k.to_sym] = v }

    PluginStore.set("custom_wizard_api_#{api_name}", "authorization", data)

    self.get(api_name)
  end

  def self.get(api_name, opts = {})
    api_name = api_name.underscore

    if data = PluginStore.get("custom_wizard_api_#{api_name}", "authorization")
      if opts[:data_only]
        data
      else
        self.new(api_name, data)
      end
    else
      nil
    end
  end

  def self.remove(api_name)
    PluginStore.remove("custom_wizard_api_#{api_name}", "authorization")
  end

  def self.authorization_string(name)
    auth = CustomWizard::Api::Authorization.get(name)
    raise Discourse::InvalidParameters.new(:name) if auth.blank?

    if auth.auth_type === "basic"
      raise Discourse::InvalidParameters.new(:username) if auth.username.blank?
      raise Discourse::InvalidParameters.new(:password) if auth.password.blank?
      "Basic #{Base64.strict_encode64((auth.username + ":" + auth.password).chomp)}"
    elsif %w[oauth_3 oauth_2].include?(auth.auth_type)
      raise Discourse::InvalidParameters.new(auth.access_token) if auth.access_token.blank?
      "Bearer #{auth.access_token}"
    else
      nil
    end
  end

  def self.get_token(name, opts = {})
    authorization = CustomWizard::Api::Authorization.get(name)
    type = authorization.auth_type

    body = {}

    if opts[:refresh] && type === "oauth_3"
      body["grant_type"] = "refresh_token"
    elsif type === "oauth_2"
      body["grant_type"] = "client_credentials"
    elsif type === "oauth_3"
      body["grant_type"] = "authorization_code"
    end

    unless opts[:refresh]
      body["client_id"] = authorization.client_id
      body["client_secret"] = authorization.client_secret
    end

    if type === "oauth_3"
      body["code"] = authorization.code
      body["redirect_uri"] = Discourse.base_url + "/admin/wizards/apis/#{name}/redirect"
    end

    connection =
      Excon.new(
        authorization.token_url,
        headers: {
          "Content-Type" => "application/x-www-form-urlencoded",
        },
        method: "GET",
        query: URI.encode_www_form(body),
      )
    begin
      result = connection.request()
      log_params = {
        time: Time.now,
        user_id: 0,
        status: "SUCCESS",
        url: authorization.token_url,
        error: "",
      }
      CustomWizard::Api::LogEntry.set(name, log_params)
    rescue SystemCallError => e
      log_params = {
        time: Time.now,
        user_id: 0,
        status: "FAILURE",
        url: authorization.token_url,
        error: "Token refresh request failed: #{e.inspect}",
      }
      CustomWizard::Api::LogEntry.set(name, log_params)
    end

    self.handle_token_result(name, result)
  end

  def self.handle_token_result(name, result)
    result_data = JSON.parse(result.body)

    return result_data if result_data["error"]

    data = {}

    data["access_token"] = result_data["access_token"]
    data["refresh_token"] = result_data["refresh_token"] if result_data["refresh_token"]
    data["token_type"] = result_data["token_type"] if result_data["token_type"]

    if result_data["expires_in"]
      data["token_expires_at"] = Time.now + result_data["expires_in"].seconds
      data["token_refresh_at"] = data["token_expires_at"].to_time - 10.minutes

      opts = { name: name }

      Jobs.enqueue_at(data["token_refresh_at"], :refresh_api_access_token, opts)
    end

    CustomWizard::Api::Authorization.set(name, data)
  end
end
