/*
 * asklyphe-frontend routes/mod.rs
 * - module file for http routes + some commonly used functions
 *
 * Copyright (C) 2025 Real Microsoft, LLC
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

use std::sync::Arc;
use askama::Template;
use askama_axum::IntoResponse;
use asklyphe_common::nats::authservice::{AuthServiceQuery, AuthServiceRequest, AuthServiceResponse};
use asklyphe_common::nats::authservice::profile::{UserInfoRequest, UserInfoResponse};
use asklyphe_common::nats::comms;
use asklyphe_common::nats::comms::ServiceResponse;
use async_nats::jetstream;
use axum::http::StatusCode;
use serde::Serialize;
use tracing::error;

pub mod search;
pub mod index;
pub mod semaphore;
pub mod user_settings;
pub mod admin;
pub mod announcement;

#[derive(Serialize)]
pub struct UserInfo {
    pub username: String,
    pub email: String,
    pub new_email: Option<String>,
    pub theme: String,
    pub administrator: bool,
}

pub async fn authenticate_user(nats: Arc<jetstream::Context>, token: String) -> Result<UserInfo, String> {
    let response = comms::query_service(
        comms::Query::AuthService(AuthServiceQuery {
            request: AuthServiceRequest::UserInfoRequest(UserInfoRequest {
                token,
            }),
            replyto: "".to_string(),
        }),
        &nats,
        false,
    ).await;
    if let Err(e) = response {
        error!("nats error: {:?}", e);
        Err("internal server error".to_string())
    } else {
        let response = response.unwrap();
        let mut internal_server_error = false;
        match &response {
            ServiceResponse::SearchService(_) => {
                error!("sent search service response when asking for auth service!! investigate ASAP!!!");
                internal_server_error = true;
            }
            ServiceResponse::BingService(_) => {
                error!("sent bing service response when asking for auth service!! investigate ASAP!!!");
                internal_server_error = true;
            }
            ServiceResponse::AuthService(r) => match r {
                AuthServiceResponse::UserInfoResponse(_) => {}
                x => {
                    error!("auth service gave {} to our user info request!", x);
                    internal_server_error = true;
                }
            },
        }
        let info = if internal_server_error {
            return Err("internal server error".to_string());
        } else {
            match response {
                ServiceResponse::AuthService(AuthServiceResponse::UserInfoResponse(r)) => match r {
                    UserInfoResponse::Success(ui) => {
                        UserInfo {
                            username: ui.username,
                            email: ui.email,
                            new_email: ui.new_email,
                            theme: ui.theme,
                            administrator: ui.administrator,
                        }
                    }
                    UserInfoResponse::InternalServerError(e) => {
                        error!("internal server error while querying user data! {e}");
                        return Err("internal server error".to_string());
                    }
                    UserInfoResponse::TokenExpired => {
                        return Err("your session has expired! please log in and try again!".to_string());
                    }
                    UserInfoResponse::TokenInvalid => {
                        return Err("your session has expired! please log in and try again!".to_string());
                    }
                    UserInfoResponse::AccountSuspended => {
                        return Err("unfortunately your account has been suspended, please check our support pages for more info".to_string());
                    }
                }
                _ => unreachable!()
            }
        };
        Ok(info)
    }
}

#[derive(Template)]
#[template(path = "404.html")]
pub struct NotFoundTemplate;

pub async fn not_found() -> impl IntoResponse {
    (StatusCode::NOT_FOUND, NotFoundTemplate).into_response()
}