// ABOUTME: Tenant-aware fitness provider factory for multi-tenant OAuth credential management
// ABOUTME: Routes provider requests through tenant-specific OAuth credentials and rate limiting
//
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) 2025 Pierre Fitness Intelligence
use crate::database_plugins::DatabaseProvider;
use crate::errors::{AppError, AppResult};
use crate::models::{Activity, Athlete, PersonalRecord, Stats};
use crate::tenant::{TenantContext, TenantOAuthClient};
use async_trait::async_trait;
use std::sync::Arc;
/// Tenant-aware fitness provider that wraps existing providers with tenant context
#[async_trait]
pub trait TenantFitnessProvider: Send + Sync {
/// Authenticate using tenant-specific OAuth credentials
async fn authenticate_tenant(
&mut self,
tenant_context: &TenantContext,
provider: &str,
database: &dyn DatabaseProvider,
) -> AppResult<()>;
/// Get athlete information for the authenticated tenant user
async fn get_athlete(&self) -> AppResult<Athlete>;
/// Get activities for the authenticated tenant user
async fn get_activities(
&self,
limit: Option<usize>,
offset: Option<usize>,
) -> AppResult<Vec<Activity>>;
/// Get specific activity by ID
async fn get_activity(&self, id: &str) -> AppResult<Activity>;
/// Get stats for the authenticated tenant user
async fn get_stats(&self) -> AppResult<Stats>;
/// Get personal records for the authenticated tenant user
async fn get_personal_records(&self) -> AppResult<Vec<PersonalRecord>>;
/// Get provider name
fn provider_name(&self) -> &'static str;
}
/// Factory for creating tenant-aware fitness providers
pub struct TenantProviderFactory {
oauth_client: Arc<TenantOAuthClient>,
}
impl TenantProviderFactory {
/// Create new tenant provider factory
#[must_use]
pub const fn new(oauth_client: Arc<TenantOAuthClient>) -> Self {
Self { oauth_client }
}
/// Create tenant-aware provider for the specified type
///
/// # Errors
///
/// Returns an error if the provider type is not supported
pub fn create_tenant_provider(
&self,
provider_type: &str,
) -> AppResult<Box<dyn TenantFitnessProvider>> {
match provider_type.to_lowercase().as_str() {
"strava" => Ok(Box::new(super::strava_tenant::TenantStravaProvider::new(
self.oauth_client.clone(),
))),
_ => Err(AppError::invalid_input(format!(
"Unknown tenant provider: {provider_type}. Currently supported: strava"
))),
}
}
}