use axum::{
	Json, Router,
	extract::{Request, State},
	middleware::{self, Next},
	response::{IntoResponse, Response},
	routing::method_routing::{get, post},
};
use const_str::concat;
use http::StatusCode;
use serde_json::json;
pub(crate) use server_metadata::*;

mod device;
mod register_client;
mod server_metadata;
mod token;

const BASE_PATH: &str = concat!(conduwuit_core::ROUTE_PREFIX, "/oauth2/");
const AUTH_CODE_PATH: &str = "grant/authorization_code";
const JWKS_URI_PATH: &str = "client/keys.json";
const CLIENT_REGISTER_PATH: &str = "client/register";
const TOKEN_REVOKE_PATH: &str = "client/revoke";
const TOKEN_PATH: &str = "grant/token";
const DEVICE_AUTHORIZATION_PATH: &str = "device";
const ACCOUNT_MANAGEMENT_PATH: &str = concat!(conduwuit_core::ROUTE_PREFIX, "/account/deeplink");

pub(crate) fn router(state: crate::State) -> Router<crate::State> {
	Router::new()
		.nest(BASE_PATH, oauth_router())
		.route(
			"/.well-known/openid-configuration",
			get(
				// TODO(unspecced): used by old versions of the matrix-js-sdk
				async |State(services): State<crate::State>| {
					Json(authorization_server_metadata(&services).await)
				},
			),
		)
		.layer(middleware::from_fn_with_state(
			state,
			async |State(state): State<crate::State>, request: Request, next: Next| -> Response {
				if state.config.oauth.compatibility_mode().oauth_available() {
					next.run(request).await
				} else {
					(StatusCode::NOT_FOUND, "OAuth is unavailable on this server").into_response()
				}
			},
		))
}

fn oauth_router() -> Router<crate::State> {
	Router::new()
		.route(concat!("/", CLIENT_REGISTER_PATH), post(register_client::register_client_route))
		// TODO(unspecced): used by old versions of the matrix-js-sdk
		.route(concat!("/", JWKS_URI_PATH), get(async || Json(json!({"keys": []}))))
		.route(concat!("/", TOKEN_PATH), post(token::token_route))
		.route(concat!("/", TOKEN_REVOKE_PATH), post(token::revoke_token_route))
		.route(concat!("/", DEVICE_AUTHORIZATION_PATH), post(device::device_authorization_route))
}
