use crate::error::RoomError;
use crate::service::RoomService;
use crate::ws_context::WsUserContext;
use chrono::Utc;
use models::rooms::NotificationType;
use models::rooms::room_message_edit_history;
use models::users::user as user_model;
use sea_orm::*;
use uuid::Uuid;

#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
pub struct MessageEditHistoryEntry {
    pub old_content: String,
    pub new_content: String,
    pub edited_at: chrono::DateTime<Utc>,
}

#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
pub struct MessageEditHistoryResponse {
    pub message_id: Uuid,
    pub history: Vec<MessageEditHistoryEntry>,
    pub total_edits: i64,
}

#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
pub struct MentionNotificationResponse {
    pub message_id: Uuid,
    pub mentioned_by: Uuid,
    pub mentioned_by_name: String,
    pub content_preview: String,
    pub room_id: Uuid,
    pub room_name: String,
    pub created_at: chrono::DateTime<Utc>,
}

#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
pub struct DraftResponse {
    pub room_id: Uuid,
    pub content: String,
    pub saved_at: chrono::DateTime<Utc>,
}

#[derive(Debug, Clone, serde::Deserialize)]
pub struct DraftSaveRequest {
    pub content: String,
}

impl RoomService {
    pub async fn save_message_edit_history(
        &self,
        message_id: Uuid,
        user_id: Uuid,
        old_content: String,
        new_content: String,
    ) -> Result<(), RoomError> {
        let history = room_message_edit_history::ActiveModel {
            id: Set(Uuid::now_v7()),
            message: Set(message_id),
            user: Set(user_id),
            old_content: Set(old_content),
            new_content: Set(new_content),
            edited_at: Set(Utc::now()),
        };

        history.insert(&self.db).await?;

        Ok(())
    }

    pub async fn get_message_edit_history(
        &self,
        message_id: Uuid,
        ctx: &WsUserContext,
    ) -> Result<MessageEditHistoryResponse, RoomError> {
        let user_id = ctx.user_id;

        let message = models::rooms::room_message::Entity::find_by_id(message_id)
            .one(&self.db)
            .await?
            .ok_or_else(|| RoomError::NotFound("Message not found".to_string()))?;

        self.require_room_member(message.room, user_id).await?;

        let history = room_message_edit_history::Entity::find()
            .filter(room_message_edit_history::Column::Message.eq(message_id))
            .order_by_asc(room_message_edit_history::Column::EditedAt)
            .all(&self.db)
            .await?;

        let total_edits = history.len() as i64;
        let history_entries = history
            .into_iter()
            .map(|h| MessageEditHistoryEntry {
                old_content: h.old_content,
                new_content: h.new_content,
                edited_at: h.edited_at,
            })
            .collect();

        Ok(MessageEditHistoryResponse {
            message_id,
            history: history_entries,
            total_edits,
        })
    }

    pub async fn get_mention_notifications(
        &self,
        limit: Option<u64>,
        ctx: &WsUserContext,
    ) -> Result<Vec<MentionNotificationResponse>, RoomError> {
        let user_id = ctx.user_id;

        let limit = limit.unwrap_or(50);

        let notifications = models::rooms::room_notifications::Entity::find()
            .filter(models::rooms::room_notifications::Column::UserId.eq(user_id))
            .filter(
                models::rooms::room_notifications::Column::NotificationType
                    .eq(NotificationType::Mention),
            )
            .order_by_desc(models::rooms::room_notifications::Column::CreatedAt)
            .limit(limit)
            .all(&self.db)
            .await?;

        let mut result = Vec::new();
        for notification in notifications {
            let mentioned_by =
                user_model::Entity::find_by_id(notification.related_user_id.unwrap_or_default())
                    .one(&self.db)
                    .await?;

            let room_name = if let Some(room_id) = notification.room {
                models::rooms::room::Entity::find_by_id(room_id)
                    .one(&self.db)
                    .await?
                    .map(|r| r.room_name)
                    .unwrap_or_else(|| "Unknown Room".to_string())
            } else {
                "Unknown Room".to_string()
            };

            let mentioned_by_name = mentioned_by
                .map(|u| u.display_name.unwrap_or(u.username))
                .unwrap_or_else(|| "Unknown User".to_string());

            let content_preview = notification
                .content
                .unwrap_or_default()
                .chars()
                .take(100)
                .collect();

            result.push(MentionNotificationResponse {
                message_id: notification.related_message_id.unwrap_or_default(),
                mentioned_by: notification.related_user_id.unwrap_or_default(),
                mentioned_by_name,
                content_preview,
                room_id: notification.room.unwrap_or_default(),
                room_name,
                created_at: notification.created_at,
            });
        }

        Ok(result)
    }

    pub async fn mark_mention_notifications_read(
        &self,
        ctx: &WsUserContext,
    ) -> Result<(), RoomError> {
        let user_id = ctx.user_id;

        use sea_orm::sea_query::Expr;

        let now = Utc::now();
        models::rooms::room_notifications::Entity::update_many()
            .col_expr(
                models::rooms::room_notifications::Column::IsRead,
                Expr::value(true),
            )
            .col_expr(
                models::rooms::room_notifications::Column::ReadAt,
                Expr::value(Some(now)),
            )
            .filter(models::rooms::room_notifications::Column::UserId.eq(user_id))
            .filter(
                models::rooms::room_notifications::Column::NotificationType
                    .eq(NotificationType::Mention),
            )
            .filter(models::rooms::room_notifications::Column::IsRead.eq(false))
            .exec(&self.db)
            .await?;

        Ok(())
    }
}
