//! Endpoints scoped to the *current user*. Kept separate from `auth::handlers` because
//! these aren't about acquiring / refreshing a session — they're about reading my own
//! state once I'm already signed in.
//!
//! Current routes:
//! - `GET /api/v1/me/context` — bundled profile + feature flags + privacy note. The
//!   account page loads this once on mount instead of issuing several round trips.
//! - `GET /api/v1/me/quota`  — live per-user storage quota estimate.

use axum::Json;
use axum::extract::State;
use serde::Serialize;

use crate::auth::middleware::AuthUser;
use crate::error::AppError;
use crate::handlers::upload::compute_storage_quota;
use crate::models::user::{User, UserRole};
use crate::services::config;
use crate::state::AppState;

#[derive(Serialize)]
pub struct QuotaDto {
    pub enabled: bool,
    pub used_bytes: i64,
    pub limit_bytes: Option<i64>,
    pub active_uploaders: i64,
    pub free_disk_bytes: i64,
}

pub async fn get_quota(
    State(state): State<AppState>,
    auth: AuthUser,
) -> Result<Json<QuotaDto>, AppError> {
    let user = User::find_by_id(&state.pool, auth.user_id)
        .await?
        .ok_or_else(|| AppError::NotFound("Benutzer nicht gefunden.".into()))?;

    let estimate = compute_storage_quota(&state).await;

    // Raw server telemetry (free disk, concurrent uploader count) is staff-only — it
    // must never reach a guest, even though the guest upload UI no longer renders it.
    // A guest still gets their own `used`/`limit` so enforcement stays transparent to
    // the code paths that consume it; only the server-wide fields are zeroed.
    let is_staff = matches!(auth.role, UserRole::Host | UserRole::Admin);

    Ok(Json(QuotaDto {
        enabled: estimate.limit_bytes.is_some(),
        used_bytes: user.total_upload_bytes,
        limit_bytes: estimate.limit_bytes,
        active_uploaders: if is_staff {
            estimate.active_uploaders
        } else {
            0
        },
        free_disk_bytes: if is_staff {
            estimate.free_disk_bytes
        } else {
            0
        },
    }))
}

#[derive(Serialize)]
pub struct MeContextDto {
    pub user_id: uuid::Uuid,
    pub display_name: String,
    pub role: String,
    /// Plain-text Datenschutzhinweis set by the admin. Empty string when not configured.
    pub privacy_note: String,
    pub quota_enabled: bool,
    pub storage_quota_enabled: bool,
    /// Uploads are locked (event closed) — the composer should show a locked state live
    /// instead of letting a guest compose an upload only to eat a 403.
    pub uploads_locked: bool,
    /// The gallery has been released and the export snapshotted — uploads are permanently
    /// closed for this run (release ⇒ lock, and reopening regenerates).
    pub gallery_released: bool,
    /// This guest is banned: a deliberately READ-ONLY ban (see `handlers/host.rs`) — they keep
    /// the feed and the keepsake, but every write is refused.
    ///
    /// Exposed so the UI can SAY so. Without it the client had no idea, so the upload button,
    /// the like button and "Löschen" all rendered enabled and returned 403 "Du bist gesperrt."
    /// on every tap — a guest tapping upload repeatedly with nobody to ask. The lock case
    /// (`uploads_locked`) has always been surfaced for exactly this reason; a ban was not.
    pub is_banned: bool,
}

pub async fn get_context(
    State(state): State<AppState>,
    auth: AuthUser,
) -> Result<Json<MeContextDto>, AppError> {
    let user = User::find_by_id(&state.pool, auth.user_id)
        .await?
        .ok_or_else(|| AppError::NotFound("Benutzer nicht gefunden.".into()))?;

    let privacy_note = config::get_str(&state.config_cache, "privacy_note", "").await;
    let quota_enabled = config::get_bool(&state.config_cache, "quota_enabled", true).await;
    let storage_quota_enabled =
        config::get_bool(&state.config_cache, "storage_quota_enabled", true).await;

    let event =
        crate::models::event::Event::find_by_slug(&state.pool, &state.config.event_slug).await?;
    let uploads_locked = event
        .as_ref()
        .map(|e| e.uploads_locked_at.is_some())
        .unwrap_or(false);
    let gallery_released = event
        .as_ref()
        .map(|e| e.export_released_at.is_some())
        .unwrap_or(false);

    Ok(Json(MeContextDto {
        user_id: user.id,
        display_name: user.display_name,
        role: user.role.as_str().to_string(),
        privacy_note,
        quota_enabled,
        storage_quota_enabled,
        uploads_locked,
        gallery_released,
        is_banned: user.is_banned,
    }))
}
