module ShopifyApp::TokenExchange

def activate_shopify_session

def activate_shopify_session
  if current_shopify_session.blank?
    retrieve_session_from_token_exchange
  end
  if ShopifyApp.configuration.check_session_expiry_date && current_shopify_session.expired?
    @current_shopify_session = nil
    retrieve_session_from_token_exchange
  end
  begin
    ShopifyApp::Logger.debug("Activating Shopify session")
    ShopifyAPI::Context.activate_session(current_shopify_session)
    yield
  ensure
    ShopifyApp::Logger.debug("Deactivating session")
    ShopifyAPI::Context.deactivate_session
  end
end

def current_shopify_domain

def current_shopify_domain
  return if params[:shop].blank?
  ShopifyApp::Utils.sanitize_shop_domain(params[:shop])
end

def current_shopify_session

def current_shopify_session
  @current_shopify_session ||= begin
    session_id = ShopifyAPI::Utils::SessionUtils.current_session_id(
      request.headers["HTTP_AUTHORIZATION"],
      nil,
      online_token_configured?,
    )
    return nil unless session_id
    ShopifyApp::SessionRepository.load_session(session_id)
  end
end

def exchange_token(shop:, session_token:, requested_token_type:)

def exchange_token(shop:, session_token:, requested_token_type:)
  if session_token.blank?
    # respond_to_invalid_session_token
    return
  end
  begin
    session = ShopifyAPI::Auth::TokenExchange.exchange_token(
      shop: shop,
      session_token: session_token,
      requested_token_type: requested_token_type,
    )
  rescue ShopifyAPI::Errors::InvalidJwtTokenError
    # respond_to_invalid_session_token
    return
  rescue ShopifyAPI::Errors::HttpResponseError => error
    ShopifyApp::Logger.error(
      "A #{error.code} error (#{error.class}) occurred during the token exchange. Response: #{error.response.body}",
    )
    raise
  rescue => error
    ShopifyApp::Logger.error("An error occurred during the token exchange: #{error.message}")
    raise
  end
  if session
    begin
      ShopifyApp::SessionRepository.store_session(session)
    rescue ActiveRecord::RecordNotUnique
      ShopifyApp::Logger.debug("Session not stored due to concurrent token exchange calls")
    end
  end
  session
end

def id_token_header

def id_token_header
  request.headers["HTTP_AUTHORIZATION"]&.match(/^Bearer (.+)$/)&.[](1)
end

def online_token_configured?

def online_token_configured?
  ShopifyApp.configuration.online_token_configured?
end

def respond_to_invalid_session_token

def respond_to_invalid_session_token
  # TODO: Implement this method to handle invalid session tokens
  # if request.xhr?
  # response.set_header("X-Shopify-Retry-Invalid-Session-Request", 1)
  # unauthorized_response = { message: :unauthorized }
  # render(json: { errors: [unauthorized_response] }, status: :unauthorized)
  # else
  # patch_session_token_url = "#{ShopifyAPI::Context.host}/patch_session_token"
  # patch_session_token_params = request.query_parameters.except(:id_token)
  # bounce_url = "#{ShopifyAPI::Context.host}#{request.path}?#{patch_session_token_params.to_query}"
  # # App Bridge will trigger a fetch to the URL in shopify-reload, with a new session token in headers
  # patch_session_token_params["shopify-reload"] = bounce_url
  # redirect_to("#{patch_session_token_url}?#{patch_session_token_params.to_query}", allow_other_host: true)
  # end
end

def retrieve_session_from_token_exchange

def retrieve_session_from_token_exchange
  # TODO: Right now JWT Middleware only updates env['jwt.shopify_domain'] from request headers tokens,
  # which won't work for new installs.
  # we need to update the middleware to also update the env['jwt.shopify_domain'] from the query params
  domain = ShopifyApp::JWT.new(session_token).shopify_domain
  ShopifyApp::Logger.info("Performing Token Exchange for [#{domain}] - (Offline)")
  session = exchange_token(
    shop: domain, # TODO: use jwt_shopify_domain ?
    session_token: session_token,
    requested_token_type: ShopifyAPI::Auth::TokenExchange::RequestedTokenType::OFFLINE_ACCESS_TOKEN,
  )
  if session && online_token_configured?
    ShopifyApp::Logger.info("Performing Token Exchange for [#{domain}] - (Online)")
    session = exchange_token(
      shop: domain, # TODO: use jwt_shopify_domain ?
      session_token: session_token,
      requested_token_type: ShopifyAPI::Auth::TokenExchange::RequestedTokenType::ONLINE_ACCESS_TOKEN,
    )
  end
  ShopifyApp.configuration.post_authenticate_tasks.perform(session)
end

def session_token

def session_token
  @session_token ||= id_token_header
end