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

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

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct UserPrivacyConfig {
    pub profile_visibility: String,
    pub email_visibility: String,
    pub activity_visibility: String,
    pub allow_search_indexing: bool,
    pub allow_direct_messages: bool,
    pub show_online_status: bool,
}

#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
pub struct UpdateUserPrivacyConfig {
    pub profile_visibility: Option<String>,
    pub email_visibility: Option<String>,
    pub activity_visibility: Option<String>,
    pub allow_search_indexing: Option<bool>,
    pub allow_direct_messages: Option<bool>,
    pub show_online_status: Option<bool>,
}

impl AppService {
    pub async fn user_update_privacy_config(
        &self,
        ctx: &Session,
        params: UpdateUserPrivacyConfig,
    ) -> Result<UserPrivacyConfig, AppError> {
        let user_uid = session_user(ctx)?;
        let mut config = self.user_privacy_config(user_uid).await?;
        if let Some(profile_visibility) = params.profile_visibility {
            config.profile_visibility = profile_visibility;
        }
        if let Some(email_visibility) = params.email_visibility {
            config.email_visibility = email_visibility;
        }
        if let Some(activity_visibility) = params.activity_visibility {
            config.activity_visibility = activity_visibility;
        }
        if let Some(allow_search_indexing) = params.allow_search_indexing {
            config.allow_search_indexing = allow_search_indexing;
        }
        if let Some(allow_direct_messages) = params.allow_direct_messages {
            config.allow_direct_messages = allow_direct_messages;
        }
        if let Some(show_online_status) = params.show_online_status {
            config.show_online_status = show_online_status;
        }
        let now = chrono::Utc::now();
        sqlx::query(
            "INSERT INTO user_privacy \
                (\"user\", profile_visibility, email_visibility, activity_visibility, allow_search_indexing, allow_direct_messages, show_online_status, created_at, updated_at) \
             VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $8) \
             ON CONFLICT (\"user\") DO UPDATE SET \
                profile_visibility = EXCLUDED.profile_visibility, email_visibility = EXCLUDED.email_visibility, \
                activity_visibility = EXCLUDED.activity_visibility, allow_search_indexing = EXCLUDED.allow_search_indexing, \
                allow_direct_messages = EXCLUDED.allow_direct_messages, show_online_status = EXCLUDED.show_online_status, \
                updated_at = EXCLUDED.updated_at",
        )
        .bind(user_uid)
        .bind(&config.profile_visibility)
        .bind(&config.email_visibility)
        .bind(&config.activity_visibility)
        .bind(config.allow_search_indexing)
        .bind(config.allow_direct_messages)
        .bind(config.show_online_status)
        .bind(now)
        .execute(self.db.writer())
        .await
        .map_err(|e| AppError::DatabaseError(e.to_string()))?;
        Ok(config)
    }

    pub async fn user_privacy_config(
        &self,
        user_uid: uuid::Uuid,
    ) -> Result<UserPrivacyConfig, AppError> {
        let row = 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_uid)
        .fetch_optional(self.db.reader())
        .await
        .map_err(|e| AppError::DatabaseError(e.to_string()))?;
        Ok(row.map(Into::into).unwrap_or_default())
    }
}

impl Default for UserPrivacyConfig {
    fn default() -> Self {
        Self {
            profile_visibility: "public".to_string(),
            email_visibility: "private".to_string(),
            activity_visibility: "public".to_string(),
            allow_search_indexing: true,
            allow_direct_messages: true,
            show_online_status: true,
        }
    }
}

impl From<UserPrivacyModel> for UserPrivacyConfig {
    fn from(value: UserPrivacyModel) -> Self {
        Self {
            profile_visibility: value.profile_visibility,
            email_visibility: value.email_visibility,
            activity_visibility: value.activity_visibility,
            allow_search_indexing: value.allow_search_indexing,
            allow_direct_messages: value.allow_direct_messages,
            show_online_status: value.show_online_status,
        }
    }
}
