use serde::{Deserialize, Serialize};

/// Basic user display data used by frontend components.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UserData {
    pub name: String,
}

/// Authentication information returned by the `check_auth` server function.
///
/// The frontend uses this to determine whether the user is logged in
/// and to display their profile (name, email, avatar).
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct AuthInfo {
    /// Whether the user has a valid session
    pub authenticated: bool,
    /// Keycloak subject identifier (unique user ID)
    pub sub: String,
    /// User email address
    pub email: String,
    /// User display name
    pub name: String,
    /// Avatar URL (from Keycloak picture claim)
    pub avatar_url: String,
    /// LibreChat instance URL for the sidebar chat link
    pub librechat_url: String,
    /// LangGraph agent builder URL (empty if not configured)
    pub langgraph_url: String,
    /// LangFlow visual workflow builder URL (empty if not configured)
    pub langflow_url: String,
    /// Langfuse observability URL (empty if not configured)
    pub langfuse_url: String,
    /// Compliance scanner URL (empty if not configured)
    pub compliance_scanner_url: String,
}

/// Per-user LLM provider configuration stored in MongoDB.
///
/// Controls which provider and model the user's chat sessions default
/// to, and stores API keys for non-LiteLLM providers.
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct UserProviderConfig {
    /// Default provider name (e.g. "litellm", "openai")
    pub default_provider: String,
    /// Default model ID (e.g. "qwen3-32b", "gpt-4o")
    pub default_model: String,
    /// OpenAI API key (empty if not configured)
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub openai_api_key: Option<String>,
    /// Anthropic API key
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub anthropic_api_key: Option<String>,
    /// HuggingFace API key
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub huggingface_api_key: Option<String>,
    /// Custom LiteLLM URL override (empty = use server default)
    pub litellm_url_override: String,
}

/// Per-user preferences stored in MongoDB.
///
/// Keyed by `sub` (Keycloak subject) and optionally scoped to an org.
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct UserPreferences {
    /// Keycloak subject identifier
    pub sub: String,
    /// Organization ID (from Keycloak Organizations)
    pub org_id: String,
    /// User-selected news/search topics
    pub custom_topics: Vec<String>,
    /// Per-user LiteLLM URL override (empty = use server default)
    pub litellm_url_override: String,
    /// Per-user LiteLLM model override (empty = use server default)
    pub litellm_model_override: String,
    /// Recently searched queries for quick access
    pub recent_searches: Vec<String>,
    /// LLM provider configuration
    #[serde(default)]
    pub provider_config: UserProviderConfig,
}

#[cfg(test)]
mod tests {
    use super::*;
    use pretty_assertions::assert_eq;

    #[test]
    fn user_data_default() {
        let ud = UserData::default();
        assert_eq!(ud.name, "");
    }

    #[test]
    fn auth_info_default_not_authenticated() {
        let info = AuthInfo::default();
        assert!(!info.authenticated);
        assert_eq!(info.sub, "");
        assert_eq!(info.email, "");
        assert_eq!(info.name, "");
        assert_eq!(info.avatar_url, "");
        assert_eq!(info.librechat_url, "");
        assert_eq!(info.langgraph_url, "");
        assert_eq!(info.langflow_url, "");
        assert_eq!(info.langfuse_url, "");
        assert_eq!(info.compliance_scanner_url, "");
    }

    #[test]
    fn auth_info_serde_round_trip() {
        let info = AuthInfo {
            authenticated: true,
            sub: "sub-123".into(),
            email: "test@example.com".into(),
            name: "Test User".into(),
            avatar_url: "https://example.com/avatar.png".into(),
            librechat_url: "https://chat.example.com".into(),
            langgraph_url: "http://localhost:8123".into(),
            langflow_url: "http://localhost:7860".into(),
            langfuse_url: "http://localhost:3000".into(),
            compliance_scanner_url: "http://localhost:9090".into(),
        };
        let json = serde_json::to_string(&info).expect("serialize AuthInfo");
        let back: AuthInfo = serde_json::from_str(&json).expect("deserialize AuthInfo");
        assert_eq!(info, back);
    }

    #[test]
    fn user_preferences_default() {
        let prefs = UserPreferences::default();
        assert_eq!(prefs.sub, "");
        assert_eq!(prefs.org_id, "");
        assert!(prefs.custom_topics.is_empty());
        assert!(prefs.recent_searches.is_empty());
    }

    #[test]
    fn user_provider_config_optional_keys_skip_none() {
        let cfg = UserProviderConfig {
            default_provider: "litellm".into(),
            default_model: "qwen3-32b".into(),
            openai_api_key: None,
            anthropic_api_key: None,
            huggingface_api_key: None,
            litellm_url_override: String::new(),
        };
        let json = serde_json::to_string(&cfg).expect("serialize UserProviderConfig");
        assert!(!json.contains("openai_api_key"));
        assert!(!json.contains("anthropic_api_key"));
        assert!(!json.contains("huggingface_api_key"));
    }

    #[test]
    fn user_provider_config_serde_round_trip_with_keys() {
        let cfg = UserProviderConfig {
            default_provider: "openai".into(),
            default_model: "gpt-4o".into(),
            openai_api_key: Some("sk-test".into()),
            anthropic_api_key: Some("ak-test".into()),
            huggingface_api_key: None,
            litellm_url_override: "http://custom:4000".into(),
        };
        let json = serde_json::to_string(&cfg).expect("serialize");
        let back: UserProviderConfig = serde_json::from_str(&json).expect("deserialize");
        assert_eq!(cfg, back);
    }
}
