//! Generated by `empu make:auth` — session login with **Argon2id** (`purwa_auth::hash_password`).
//!
//! **Cargo.toml**
//! - `purwa = { path = "…", features = ["auth"] }` (or crates.io version with `auth`)
//! - `purwa-auth = { path = "…", features = ["postgres"] }`
//! - `serde`, `validator`, `axum`, `sqlx` as in your app
//!
//! **`AppState`** must include a public `pool: sqlx::PgPool` field (adjust imports if your type lives elsewhere).

use axum::extract::State;
use axum::response::{IntoResponse, Redirect};
use axum::routing::{get, post};
use axum::Router;
use purwa::ValidatedForm;
use purwa_auth::{
    insert_user, AuthManagerLayerBuilder, AuthSession, CurrentUser, EmailPasswordCredential,
    PgAuthnBackend, memory_session_layer,
};
use serde::Deserialize;
use validator::Validate;

/// Replace with your real `AppState` import.
#[derive(Clone)]
pub struct AppState {
    pub pool: sqlx::PgPool,
}

#[derive(Debug, Deserialize, Validate)]
pub struct RegisterForm {
    #[validate(email(message = "invalid email"))]
    pub email: String,
    #[validate(length(min = 8, message = "at least 8 characters"))]
    pub password: String,
}

#[derive(Debug, Deserialize, Validate)]
pub struct LoginForm {
    #[validate(email(message = "invalid email"))]
    pub email: String,
    pub password: String,
}

pub fn router(state: AppState) -> Router<AppState> {
    let backend = PgAuthnBackend::new(state.pool.clone());
    let session_layer = memory_session_layer();
    let auth_layer = AuthManagerLayerBuilder::new(backend, session_layer).build();

    Router::new()
        .route("/register", post(register))
        .route("/login", post(login))
        .route("/logout", post(logout))
        .route("/me", get(me))
        .layer(auth_layer)
        .with_state(state)
}

async fn register(
    State(state): State<AppState>,
    ValidatedForm(form): ValidatedForm<RegisterForm>,
) -> impl IntoResponse {
    match insert_user(&state.pool, &form.email, &form.password).await {
        Ok(_) => Redirect::temporary("/login").into_response(),
        Err(_) => (axum::http::StatusCode::CONFLICT, "email taken or db error").into_response(),
    }
}

async fn login(
    mut auth: AuthSession<PgAuthnBackend>,
    ValidatedForm(form): ValidatedForm<LoginForm>,
) -> impl IntoResponse {
    let creds = EmailPasswordCredential {
        email: form.email,
        password: form.password,
    };
    let user = match auth.authenticate(creds).await {
        Ok(Some(u)) => u,
        Ok(None) => {
            return (axum::http::StatusCode::UNAUTHORIZED, "invalid credentials").into_response();
        }
        Err(_) => {
            return (
                axum::http::StatusCode::INTERNAL_SERVER_ERROR,
                "database error",
            )
                .into_response();
        }
    };
    if auth.login(&user).await.is_err() {
        return (axum::http::StatusCode::INTERNAL_SERVER_ERROR, "login failed").into_response();
    }
    Redirect::temporary("/").into_response()
}

async fn logout(mut auth: AuthSession<PgAuthnBackend>) -> impl IntoResponse {
    let _ = auth.logout().await;
    Redirect::temporary("/login").into_response()
}

async fn me(CurrentUser(u): CurrentUser<PgAuthnBackend>) -> impl IntoResponse {
    format!("logged in as {} ({})", u.email, u.id)
}
