//! Public email capture for the nxtgauge.com coming-soon page
//! (nxtgauge-gitops/coming-soon/index.html's "Notify Me" form).
//! Deliberately unauthenticated - anyone can submit an email, same trust
//! model as a newsletter signup form.

use crate::AppState;
use axum::{
    extract::{Query, State},
    http::StatusCode,
    routing::{get, post},
    Json, Router,
};
use contracts::auth_middleware::{require_admin, AuthUser};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::FromRow;

pub fn router() -> Router<AppState> {
    Router::new().route("/", post(join_waitlist))
}

/// Mounted at /api/admin/waitlist by the gateway.
pub fn admin_router() -> Router<AppState> {
    Router::new().route("/", get(list_waitlist))
}

#[derive(Debug, Deserialize)]
struct JoinWaitlistRequest {
    email: String,
}

#[derive(Debug, Serialize)]
struct JoinWaitlistResponse {
    joined: bool,
}

async fn join_waitlist(
    State(state): State<AppState>,
    Json(body): Json<JoinWaitlistRequest>,
) -> Result<Json<JoinWaitlistResponse>, (StatusCode, String)> {
    let email = body.email.trim().to_lowercase();

    // Deliberately loose validation (just "has an @ and something on both
    // sides") - this is a pre-launch marketing signup, not an account, and
    // being too strict just loses real signups to typos in TLDs etc.
    let valid = {
        let mut parts = email.splitn(2, '@');
        matches!((parts.next(), parts.next()), (Some(local), Some(domain)) if !local.is_empty() && domain.contains('.'))
    };
    if !valid || email.len() > 255 {
        return Err((StatusCode::BAD_REQUEST, "Please enter a valid email address".to_string()));
    }

    sqlx::query(
        "INSERT INTO waitlist_signups (email) VALUES ($1) ON CONFLICT (email) DO NOTHING",
    )
    .bind(&email)
    .execute(&state.pool)
    .await
    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;

    Ok(Json(JoinWaitlistResponse { joined: true }))
}

#[derive(Debug, Deserialize)]
struct ListWaitlistQuery {
    page: Option<i64>,
    limit: Option<i64>,
}

#[derive(Debug, FromRow, Serialize)]
struct WaitlistSignupRow {
    email: String,
    created_at: DateTime<Utc>,
}

#[derive(Debug, Serialize)]
struct ListWaitlistResponse {
    signups: Vec<WaitlistSignupRow>,
    total: i64,
    page: i64,
    limit: i64,
}

async fn list_waitlist(
    auth: AuthUser,
    State(state): State<AppState>,
    Query(q): Query<ListWaitlistQuery>,
) -> Result<Json<ListWaitlistResponse>, (StatusCode, String)> {
    require_admin(&auth).map_err(|_| (StatusCode::FORBIDDEN, "Admin access required".to_string()))?;

    let limit = q.limit.unwrap_or(200).clamp(1, 1000);
    let page = q.page.unwrap_or(1).max(1);
    let offset = (page - 1) * limit;

    let signups = sqlx::query_as::<_, WaitlistSignupRow>(
        "SELECT email, created_at FROM waitlist_signups ORDER BY created_at DESC LIMIT $1 OFFSET $2",
    )
    .bind(limit)
    .bind(offset)
    .fetch_all(&state.pool)
    .await
    .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;

    let total: i64 = sqlx::query_scalar("SELECT count(*) FROM waitlist_signups")
        .fetch_one(&state.pool)
        .await
        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("DB error: {e}")))?;

    Ok(Json(ListWaitlistResponse { signups, total, page, limit }))
}
