use actix_web::{HttpResponse, Responder, Scope, get, post, web};
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, Set};
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::app_data::AppData;
use crate::entity::notification;
use crate::error::ApiError;
use crate::middleware::auth::AuthUser;

#[derive(Debug, Deserialize)]
pub struct ListQuery {
    pub status: Option<String>,
}

#[derive(Debug, Serialize)]
pub struct OkResponse {
    pub ok: bool,
}

#[get("")]
async fn list_notifications(
    app_data: web::Data<AppData>,
    auth: AuthUser,
    query: web::Query<ListQuery>,
) -> Result<impl Responder, ApiError> {
    let status = query.status.as_deref().unwrap_or("all");

    let mut q =
        notification::Entity::find().filter(notification::Column::RecipientId.eq(auth.user_id));

    if status == "unread" {
        q = q.filter(notification::Column::ReadAt.is_null());
    }

    let notifications = q
        .order_by_desc(notification::Column::TriggerAt)
        .all(&app_data.db)
        .await?;

    Ok(HttpResponse::Ok().json(notifications))
}

#[post("/{id}/read")]
async fn mark_read(
    app_data: web::Data<AppData>,
    auth: AuthUser,
    path: web::Path<Uuid>,
) -> Result<impl Responder, ApiError> {
    let id = path.into_inner();

    let notif = notification::Entity::find_by_id(id)
        .filter(notification::Column::RecipientId.eq(auth.user_id))
        .one(&app_data.db)
        .await?
        .ok_or_else(|| ApiError::NotFound("Not found".to_string()))?;

    let mut active: notification::ActiveModel = notif.into();
    active.read_at = Set(Some(chrono::Utc::now().fixed_offset()));
    active.updated_at = Set(chrono::Utc::now().fixed_offset());
    active.update(&app_data.db).await?;

    Ok(HttpResponse::Ok().json(OkResponse { ok: true }))
}

#[post("/read-all")]
async fn mark_all_read(
    app_data: web::Data<AppData>,
    auth: AuthUser,
) -> Result<impl Responder, ApiError> {
    let now = chrono::Utc::now().fixed_offset();

    notification::Entity::update_many()
        .filter(notification::Column::RecipientId.eq(auth.user_id))
        .filter(notification::Column::ReadAt.is_null())
        .col_expr(
            notification::Column::ReadAt,
            sea_orm::sea_query::Expr::value(now),
        )
        .col_expr(
            notification::Column::UpdatedAt,
            sea_orm::sea_query::Expr::value(now),
        )
        .exec(&app_data.db)
        .await?;

    Ok(HttpResponse::Ok().json(OkResponse { ok: true }))
}

pub fn routes() -> Scope {
    web::scope("/api/notifications")
        .service(list_notifications)
        .service(mark_all_read)
        .service(mark_read)
}
