//! Admin authentication module.
//!
//! Dual-mode authentication for the admin interface:
//! - Server staff: Uses chattyness_owner pool (bypasses RLS, full access)
//! - Realm admins: Uses chattyness_app pool (RLS enforces permissions)

#[cfg(feature = "ssr")]
use axum::{
    http::StatusCode,
    response::{IntoResponse, Redirect, Response},
};
#[cfg(feature = "ssr")]
use sqlx::PgPool;
#[cfg(feature = "ssr")]
use tower_sessions::{Expiry, SessionManagerLayer, cookie::SameSite, cookie::time::Duration};
#[cfg(feature = "ssr")]
use tower_sessions_sqlx_store::PostgresStore;
#[cfg(feature = "ssr")]
use uuid::Uuid;

#[cfg(feature = "ssr")]
use chattyness_db::models::{RealmRole, ServerRole, StaffMember, User};
#[cfg(feature = "ssr")]
use chattyness_error::AppError;

// =============================================================================
// Session Constants
// =============================================================================

/// Admin session cookie name.
pub const ADMIN_SESSION_COOKIE_NAME: &str = "chattyness_admin_session";

/// Staff ID key in admin session.
pub const ADMIN_SESSION_STAFF_ID_KEY: &str = "staff_id";

/// User ID key in session (for realm admins coming from app).
pub const SESSION_USER_ID_KEY: &str = "user_id";

// =============================================================================
// Admin Authentication
// =============================================================================

/// Realm admin role information.
#[cfg(feature = "ssr")]
#[derive(Debug, Clone)]
pub struct RealmAdminRole {
    pub realm_id: Uuid,
    pub realm_slug: String,
    pub realm_name: String,
    pub role: RealmRole,
}

/// Authenticated admin - either server staff or realm admin.
///
/// This enum determines which database pool to use:
/// - ServerStaff: Uses owner_pool (bypasses RLS)
/// - RealmAdmin: Uses app_pool (RLS enforces permissions)
#[cfg(feature = "ssr")]
#[derive(Debug, Clone)]
pub enum AdminAuth {
    /// Server staff member - full access via owner pool.
    ServerStaff { staff: StaffMember },
    /// Realm admin (owner, moderator, or builder) - scoped access via app pool with RLS.
    RealmAdmin {
        user: User,
        /// Realms where this user has admin privileges.
        realm_roles: Vec<RealmAdminRole>,
    },
}

#[cfg(feature = "ssr")]
impl AdminAuth {
    /// Get the display name for the authenticated admin.
    pub fn display_name(&self) -> &str {
        match self {
            AdminAuth::ServerStaff { staff } => &staff.display_name,
            AdminAuth::RealmAdmin { user, .. } => &user.display_name,
        }
    }

    /// Get the username for the authenticated admin.
    pub fn username(&self) -> &str {
        match self {
            AdminAuth::ServerStaff { staff } => &staff.username,
            AdminAuth::RealmAdmin { user, .. } => &user.username,
        }
    }

    /// Check if this is a server staff member.
    pub fn is_server_staff(&self) -> bool {
        matches!(self, AdminAuth::ServerStaff { .. })
    }

    /// Check if this admin can access server-wide settings.
    pub fn can_access_server_config(&self) -> bool {
        match self {
            AdminAuth::ServerStaff { staff } => {
                matches!(staff.role, ServerRole::Owner | ServerRole::Admin)
            }
            AdminAuth::RealmAdmin { .. } => false,
        }
    }

    /// Check if this admin can manage server staff.
    pub fn can_manage_staff(&self) -> bool {
        match self {
            AdminAuth::ServerStaff { staff } => matches!(staff.role, ServerRole::Owner),
            AdminAuth::RealmAdmin { .. } => false,
        }
    }

    /// Check if this admin can view all users.
    pub fn can_view_all_users(&self) -> bool {
        matches!(self, AdminAuth::ServerStaff { .. })
    }

    /// Get the realms this admin can manage (empty for server staff who see all).
    pub fn managed_realms(&self) -> &[RealmAdminRole] {
        match self {
            AdminAuth::ServerStaff { .. } => &[],
            AdminAuth::RealmAdmin { realm_roles, .. } => realm_roles,
        }
    }
}

// =============================================================================
// Admin Auth Error
// =============================================================================

/// Admin authentication errors.
#[cfg(feature = "ssr")]
#[derive(Debug)]
pub enum AdminAuthError {
    Unauthorized,
    SessionError,
    InternalError,
}

#[cfg(feature = "ssr")]
impl IntoResponse for AdminAuthError {
    fn into_response(self) -> Response {
        match self {
            AdminAuthError::Unauthorized => {
                // Redirect to login page instead of returning 401
                Redirect::to("/admin/login").into_response()
            }
            AdminAuthError::SessionError | AdminAuthError::InternalError => {
                (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response()
            }
        }
    }
}

#[cfg(feature = "ssr")]
impl From<AdminAuthError> for AppError {
    fn from(err: AdminAuthError) -> Self {
        match err {
            AdminAuthError::Unauthorized => AppError::Unauthorized,
            AdminAuthError::SessionError => AppError::Internal("Session error".to_string()),
            AdminAuthError::InternalError => AppError::Internal("Internal error".to_string()),
        }
    }
}

// =============================================================================
// Session Layer
// =============================================================================

/// Create the session management layer for admin interface.
#[cfg(feature = "ssr")]
pub async fn create_admin_session_layer(
    pool: PgPool,
    secure: bool,
) -> SessionManagerLayer<PostgresStore> {
    let session_store = PostgresStore::new(pool)
        .with_schema_name("auth")
        .expect("Invalid schema name for session store")
        .with_table_name("tower_sessions")
        .expect("Invalid table name for session store");

    // Create session table if it doesn't exist
    if let Err(e) = session_store.migrate().await {
        tracing::warn!(
            "Admin session table migration failed (may already exist): {}",
            e
        );
    }

    SessionManagerLayer::new(session_store)
        .with_name(ADMIN_SESSION_COOKIE_NAME)
        .with_secure(secure)
        .with_same_site(SameSite::Lax)
        .with_http_only(true)
        .with_expiry(Expiry::OnInactivity(Duration::hours(4)))
}
