use axum::{
    body::Body,
    extract::{Request, State},
    response::{IntoResponse, Response},
};
use leptos::*;
use leptos_axum::handle_server_fns_with_context;

use crate::{
    domain::{
        api::{ports::ApiService, prelude::FlashBag},
        leptos::app::App,
    },
    inbound::http::state::AppState,
};

pub(crate) async fn server_fn_handler<S: ApiService>(
    State(app_state): State<AppState<S>>,
    session: axum_session::SessionAnySession,
    request: Request<Body>,
) -> impl axum::response::IntoResponse {
    handle_server_fns_with_context(
        move || {
            provide_context(app_state.api_service().clone());
            provide_context(session.clone());
            provide_context(FlashBag::new(session.clone()));
        },
        request,
    )
    .await
}

pub(crate) async fn leptos_routes_handler<S: ApiService>(
    State(app_state): State<AppState<S>>,
    session: axum_session::SessionAnySession,
    req: Request<Body>,
) -> Response {
    let handler = leptos_axum::render_app_to_stream_with_context(
        app_state.leptos_options.clone(),
        move || {
            provide_context(app_state.api_service().clone());
            provide_context(session.clone());
            provide_context(FlashBag::new(session.clone()));
        },
        || view! { <App/> },
    );
    handler(req).await.into_response()
}
