use crate::AppState;
use axum::{
    extract::State,
    http::StatusCode,
    response::IntoResponse,
    routing::{get, patch},
    Json, Router,
};
use contracts::auth_middleware::AuthUser;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

pub fn router() -> Router<AppState> {
    Router::new()
        .route("/", get(get_settings))
        .route("/notifications", patch(update_notifications))
        .route("/delete-account-request", get(get_delete_account_request).post(create_delete_account_request))
}

#[derive(Serialize)]
struct SettingsResponse {
    email_notifications: bool,
    in_app_notifications: bool,
    sms_notifications: bool,
}

#[derive(Deserialize)]
struct UpdateNotificationsPayload {
    email_notifications: bool,
    in_app_notifications: bool,
    sms_notifications: bool,
}

#[derive(Deserialize)]
struct CreateDeleteAccountPayload {
    reason: Option<String>,
}

#[derive(Serialize)]
struct DeleteRequestResponse {
    status: String,
    deleted_at: Option<String>,
}

async fn ensure_settings_row(pool: &sqlx::PgPool, user_id: Uuid) -> Result<(), sqlx::Error> {
    sqlx::query(
        r#"
        INSERT INTO user_settings (user_id)
        VALUES ($1)
        ON CONFLICT (user_id) DO NOTHING
        "#,
    )
    .bind(user_id)
    .execute(pool)
    .await?;
    Ok(())
}

async fn get_settings(
    auth: AuthUser,
    State(state): State<AppState>,
) -> impl IntoResponse {
    if let Err(e) = ensure_settings_row(&state.pool, auth.user_id).await {
        return (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response();
    }

    let row = sqlx::query_as::<_, (bool, bool, bool)>(
        r#"
        SELECT email_notifications, in_app_notifications, sms_notifications
        FROM user_settings
        WHERE user_id = $1
        "#,
    )
    .bind(auth.user_id)
    .fetch_one(&state.pool)
    .await;

    match row {
        Ok((email_notifications, in_app_notifications, sms_notifications)) => (
            StatusCode::OK,
            Json(SettingsResponse {
                email_notifications,
                in_app_notifications,
                sms_notifications,
            }),
        )
            .into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

async fn update_notifications(
    auth: AuthUser,
    State(state): State<AppState>,
    Json(payload): Json<UpdateNotificationsPayload>,
) -> impl IntoResponse {
    let result = sqlx::query(
        r#"
        INSERT INTO user_settings (user_id, email_notifications, in_app_notifications, sms_notifications, updated_at)
        VALUES ($1, $2, $3, $4, NOW())
        ON CONFLICT (user_id)
        DO UPDATE SET
          email_notifications = EXCLUDED.email_notifications,
          in_app_notifications = EXCLUDED.in_app_notifications,
          sms_notifications = EXCLUDED.sms_notifications,
          updated_at = NOW()
        "#,
    )
    .bind(auth.user_id)
    .bind(payload.email_notifications)
    .bind(payload.in_app_notifications)
    .bind(payload.sms_notifications)
    .execute(&state.pool)
    .await;

    match result {
        Ok(_) => (
            StatusCode::OK,
            Json(serde_json::json!({
                "message": "Notification preferences updated"
            })),
        )
            .into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

async fn get_delete_account_request(
    auth: AuthUser,
    State(state): State<AppState>,
) -> impl IntoResponse {
    let row = sqlx::query_as::<_, (Option<chrono::DateTime<chrono::Utc>>, )>(
        r#"
        SELECT deleted_at
        FROM users
        WHERE id = $1
        "#,
    )
    .bind(auth.user_id)
    .fetch_optional(&state.pool)
    .await;

    match row {
        Ok(Some((deleted_at,))) if deleted_at.is_some() => (
            StatusCode::OK,
            Json(DeleteRequestResponse {
                status: "DELETED".to_string(),
                deleted_at: deleted_at.map(|v| v.to_rfc3339()),
            }),
        )
            .into_response(),
        Ok(Some((_deleted_at,))) => (
            StatusCode::OK,
            Json(DeleteRequestResponse {
                status: "NONE".to_string(),
                deleted_at: None,
            }),
        )
            .into_response(),
        Ok(None) => (
            StatusCode::NOT_FOUND,
            Json(serde_json::json!({ "error": "User not found" })),
        )
            .into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}

async fn create_delete_account_request(
    auth: AuthUser,
    State(state): State<AppState>,
    Json(payload): Json<CreateDeleteAccountPayload>,
) -> impl IntoResponse {
    let user = match db::models::user::UserRepository::get_by_id(&state.pool, auth.user_id).await {
        Ok(u) => u,
        Err(_) => {
            return (
                StatusCode::UNAUTHORIZED,
                Json(serde_json::json!({ "error": "User not found" })),
            )
                .into_response()
        }
    };

    let already_deleted = sqlx::query_scalar::<_, i64>(
        r#"
        SELECT COUNT(*)::BIGINT
        FROM users
        WHERE id = $1 AND deleted_at IS NOT NULL
        "#,
    )
    .bind(auth.user_id)
    .fetch_one(&state.pool)
    .await;

    if let Ok(count) = already_deleted {
        if count > 0 {
            return (
                StatusCode::CONFLICT,
                Json(serde_json::json!({
                    "error": "Account is already deleted."
                })),
            )
                .into_response();
        }
    }

    let deleted_at = chrono::Utc::now();
    let soft_deleted = sqlx::query(
        r#"
        UPDATE users
        SET deleted_at = $2, status = 'SUSPENDED', updated_at = NOW()
        WHERE id = $1 AND deleted_at IS NULL
        "#,
    )
    .bind(auth.user_id)
    .bind(deleted_at)
    .execute(&state.pool)
    .await;

    match soft_deleted {
        Ok(result) if result.rows_affected() > 0 => {
            let _ = db::models::user::UserRepository::revoke_all_for_user(&state.pool, auth.user_id).await;
            let _ = state
                .mail
                .send_account_deleted_email(
                    &user.email,
                    &format!("{} {}", user.first_name.unwrap_or_default(), user.last_name.unwrap_or_default()),
                )
                .await;
            let _ = sqlx::query(
                r#"INSERT INTO notifications (user_id, title, body, type)
                   VALUES ($1, $2, $3, $4)"#,
            )
            .bind(auth.user_id)
            .bind("Account Deleted")
            .bind(format!(
                "Your account was deleted{}.",
                payload
                    .reason
                    .as_deref()
                    .map(|r| format!(" Reason: {r}"))
                    .unwrap_or_default()
            ))
            .bind("ACCOUNT")
            .execute(&state.pool)
            .await;

            (
                StatusCode::OK,
                Json(DeleteRequestResponse {
                    status: "DELETED".to_string(),
                    deleted_at: Some(deleted_at.to_rfc3339()),
                }),
            )
                .into_response()
        }
        Ok(_) => (
            StatusCode::CONFLICT,
            Json(serde_json::json!({ "error": "Account is already deleted." })),
        )
            .into_response(),
        Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(),
    }
}
