//! # Comments helper
//!
//! @author Pierre Hubert

use crate::constants::database_tables_names::COMMENTS_TABLE;
use crate::data::comment::Comment;
use crate::data::error::{ExecError, ResultBoxError};
use crate::data::user::UserID;
use crate::helpers::{database, likes_helper};
use crate::helpers::likes_helper::LikeType;
use crate::utils::date_utils::mysql_date;
use crate::utils::user_data_utils::user_data_path;

/// Create a new comment. In case of success, this function returns the ID of the created comment
pub fn create(c: &Comment) -> ResultBoxError<u64> {
    let comment_id = database::InsertQuery::new(COMMENTS_TABLE)
        .add_u64("ID_texte", c.post_id)
        .add_user_id("ID_personne", &c.user_id)
        .add_str("date_envoi", &mysql_date())
        .add_u64("time_insert", c.time_sent)
        .add_str("commentaire", &c.content)
        .add_opt_str("image_commentaire", c.image_path.as_ref())
        .insert()?
        .ok_or(ExecError::new("No ID returned after comment creation!"))?;

    Ok(comment_id)
}

/// Get the comments of a post
pub fn get(post_id: u64) -> ResultBoxError<Vec<Comment>> {
    database::QueryInfo::new(COMMENTS_TABLE)
        .cond_u64("ID_texte", post_id)
        .set_order("ID")
        .exec(db_to_comment)
}

/// Get information about a single comment
pub fn get_single(comment_id: u64) -> ResultBoxError<Comment> {
    database::QueryInfo::new(COMMENTS_TABLE)
        .cond_u64("ID", comment_id)
        .query_row(db_to_comment)
}

/// Export all the comments of a given user
pub fn export_all_user(user_id: &UserID) -> ResultBoxError<Vec<Comment>> {
    database::QueryInfo::new(COMMENTS_TABLE)
        .cond_user_id("ID_personne", user_id)
        .exec(db_to_comment)
}


/// Turn a database entry into a comment object
fn db_to_comment(row: &database::RowResult) -> ResultBoxError<Comment> {
    Ok(Comment {
        id: row.get_u64("ID")?,
        time_sent: row.get_u64("time_insert").unwrap_or(0),
        user_id: row.get_user_id("ID_personne")?,
        post_id: row.get_u64("ID_texte")?,
        content: row.get_str("commentaire")?,
        image_path: row.get_optional_str("image_commentaire")?
            .map(|f| f.replace("file:", "")),
    })
}

/// Update comment content
pub fn edit(comment_id: u64, new_content: &str) -> ResultBoxError {
    database::UpdateInfo::new(COMMENTS_TABLE)
        .cond_u64("ID", comment_id)
        .set_str("commentaire", new_content)
        .exec()
}

/// Delete a single comment
pub fn delete(c: &Comment) -> ResultBoxError {
    // Delete associated image (if any)
    if let Some(image) = &c.image_path {
        let path = user_data_path(image.as_ref());
        if path.exists() {
            std::fs::remove_file(&path)?;
        }
    }

    // Remove the likes associated with the comment
    likes_helper::delete_all(c.id, LikeType::COMMENT)?;

    // Remove the comment from the database
    database::DeleteQuery::new(COMMENTS_TABLE)
        .cond_u64("ID", c.id)
        .exec()?;

    Ok(())
}

/// Delete all the comments associated to a post
pub fn delete_all(post_id: u64) -> ResultBoxError {
    for c in &get(post_id)? {
        delete(c)?;
    }

    Ok(())
}

/// Delete all the comments created by a user
pub fn delete_all_user(user_id: &UserID) -> ResultBoxError {
    for comment in &export_all_user(user_id)? {
        delete(comment)?;
    }

    Ok(())
}