//! HTML OAuth2 (Google) sign-in.
//!
//! The provider round-trip is handled by loco-oauth2's built-in authorize +
//! cookie-callback handlers. The callback upserts the user, stores an OAuth2
//! session, sets loco-oauth2's *private* session cookie, and redirects to the
//! configured `protected_url` — which is our [`complete`] handler. There we
//! trade the OAuth2 session for OUR Loco `auth_token` JWT cookie, so the rest of
//! the app (the Casbin layer, `guard`, the unified `/login`) treats a Google
//! user exactly like a password login. Admins (matching `ADMIN_EMAIL`) land on
//! the dashboard, everyone else on the storefront.

use loco_oauth2::controllers::{
    middleware::OAuth2CookieUser,
    oauth2::{google_authorization_url, google_callback_cookie},
};
use loco_rs::prelude::*;

use crate::{
    controllers::auth as auth_controller,
    controllers::cart,
    models::{o_auth2_sessions, users, users::OAuth2UserProfile},
    shared::guard,
};

type GoogleCookieUser = OAuth2CookieUser<OAuth2UserProfile, users::Model, o_auth2_sessions::Model>;

/// Bridge from loco-oauth2's session cookie to our own auth cookie.
#[debug_handler]
async fn complete(State(ctx): State<AppContext>, user: GoogleCookieUser) -> Result<Response> {
    let user: &users::Model = user.as_ref();
    let jwt_secret = ctx.config.get_jwt_config()?;
    let token = user
        .generate_jwt(&jwt_secret.secret, jwt_secret.expiration)
        .or_else(|_| unauthorized("unauthorized!"))?;
    let dest = if guard::is_admin(&ctx, user) {
        "/admin/dashboard"
    } else {
        "/"
    };
    let cart_cookie = cart::cart_cookie_for_user(&ctx, user.id).await?;
    format::render()
        .cookies(&[auth_controller::auth_cookie(&token, jwt_secret.expiration), cart_cookie])?
        .redirect(dest)
}

pub fn routes() -> Routes {
    Routes::new()
        .prefix("api/oauth2")
        // Redirects the browser to Google's consent screen.
        .add("/google", get(google_authorization_url))
        // Google redirects back here; loco-oauth2 exchanges the code, upserts
        // the user, and redirects to `protected_url` (/api/oauth2/protected).
        .add(
            "/google/callback/cookie",
            get(google_callback_cookie::<OAuth2UserProfile, users::Model, o_auth2_sessions::Model>),
        )
        .add("/protected", get(complete))
}
