use db::sqlx;
use model::users::{UserPrivacyModel, UserProfileModel};
use serde::{Deserialize, Serialize};

use crate::{AppService, error::AppError};

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct PublicUserResponse {
    pub username: String,
    pub display_name: String,
    pub avatar_url: String,
    pub website_url: String,
    pub language: String,
    pub timezone: String,
    pub allow_direct_messages: bool,
    pub show_online_status: bool,
}

impl AppService {
    pub async fn users_public_by_username(
        &self,
        username: &str,
    ) -> Result<PublicUserResponse, AppError> {
        let user = self.users_find_active_user_by_username(username).await?;

        let privacy = sqlx::query_as::<_, UserPrivacyModel>(
            "SELECT \"user\", profile_visibility, email_visibility, activity_visibility, allow_search_indexing, allow_direct_messages, show_online_status, created_at, updated_at \
             FROM user_privacy WHERE \"user\" = $1",
        )
        .bind(user.id)
        .fetch_optional(self.db.reader())
        .await
        .map_err(|e| AppError::DatabaseError(e.to_string()))?;

        if privacy
            .as_ref()
            .is_some_and(|privacy| privacy.profile_visibility == "private")
        {
            return Err(AppError::Forbidden("profile is private".to_string()));
        }

        let profile = sqlx::query_as::<_, UserProfileModel>(
            "SELECT \"user\", language, theme, timezone, created_at, updated_at \
             FROM user_profile WHERE \"user\" = $1",
        )
        .bind(user.id)
        .fetch_optional(self.db.reader())
        .await
        .map_err(|e| AppError::DatabaseError(e.to_string()))?;

        Ok(PublicUserResponse {
            username: user.username,
            display_name: user.display_name,
            avatar_url: user.avatar_url,
            website_url: user.website_url,
            language: profile
                .as_ref()
                .map(|profile| profile.language.clone())
                .unwrap_or_else(|| "en".to_string()),
            timezone: profile
                .map(|profile| profile.timezone)
                .unwrap_or_else(|| "UTC".to_string()),
            allow_direct_messages: privacy
                .as_ref()
                .map(|privacy| privacy.allow_direct_messages)
                .unwrap_or(true),
            show_online_status: privacy
                .map(|privacy| privacy.show_online_status)
                .unwrap_or(true),
        })
    }
}
