# frozen_string_literal: true

module DiscourseWebauthn
  class BaseValidationService
    def initialize(current_user, params, session:, factor_type:)
      @current_user = current_user
      @params = params
      @factor_type = factor_type
      @session = session
    end

    def validate_webauthn_type(type_to_check)
      return if client_data["type"] == type_to_check
      raise(InvalidTypeError, I18n.t("webauthn.validation.invalid_type_error"))
    end

    def validate_challenge
      return if challenge_match?
      raise(ChallengeMismatchError, I18n.t("webauthn.validation.challenge_mismatch_error"))
    end

    def validate_origin
      return if origin_match?
      raise(InvalidOriginError, I18n.t("webauthn.validation.invalid_origin_error"))
    end

    def validate_rp_id_hash
      return if rp_id_hash_match?
      raise(
        InvalidRelyingPartyIdError,
        I18n.t("webauthn.validation.invalid_relying_party_id_error"),
      )
    end

    ## flags per specification
    # https://www.w3.org/TR/webauthn-2/#sctn-authenticator-data
    # bit 0 - user presence
    # bit 1 - reserved for future use
    # bit 2 - user verification
    # bit 3-5 - reserved for future use
    # bit 6 - attested credential data
    # bit 7 - extension data

    def validate_user_presence
      flags = auth_data[32].unpack("b*")[0].split("")
      # bit 0 - user presence
      return if flags[0] == "1"
      raise(UserPresenceError, I18n.t("webauthn.validation.user_presence_error"))
    end

    def validate_user_verification
      flags = auth_data[32].unpack("b*")[0].split("")
      # bit 2 - user verification
      return if flags[2] == "1"
      raise(UserVerificationError, I18n.t("webauthn.validation.user_verification_error"))
    end

    private

    # https://w3c.github.io/webauthn/#sctn-registering-a-new-credential
    # Let JSONtext be the result of running UTF-8 decode on the value of response.clientDataJSON.
    def client_data_json
      @client_data_json ||= Base64.decode64(@params[:clientData])
    end

    # Let C, the client data claimed as collected during the credential creation, be the result of running
    # an implementation-specific JSON parser on JSONtext.
    def client_data
      @client_data ||= JSON.parse(client_data_json)
    end

    def challenge_match?
      Base64.decode64(client_data["challenge"]) ==
        DiscourseWebauthn.challenge(@current_user, @session)
    end

    def origin_match?
      client_data["origin"] == DiscourseWebauthn.origin
    end

    def rp_id_hash_match?
      auth_data[0..31] == OpenSSL::Digest::SHA256.digest(DiscourseWebauthn.rp_id)
    end

    def client_data_hash
      @client_data_hash ||= OpenSSL::Digest::SHA256.digest(client_data_json)
    end
  end
end