/*
 * asklyphe-auth-frontend main.rs
 * - entrypoint for the asklyphe authentication frontend
 *
 * 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/>.
*/

mod register;
mod verify;
mod login;

use std::{env, process};
use std::collections::HashMap;
use std::net::{SocketAddr, ToSocketAddrs};
use std::ops::Deref;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;
use argon2::{Argon2, PasswordHasher};
use argon2::password_hash::rand_core::OsRng;
use argon2::password_hash::SaltString;
use askama::Template;
use asklyphe_common::nats;
use asklyphe_common::nats::authservice::{AuthServiceQuery, AuthServiceRequest, AuthServiceResponse, EmailError, PasswordError, RegisterError, RegisterRequest, RegisterResponse, UsernameError};
use asklyphe_common::nats::comms;
use asklyphe_common::nats::comms::ServiceResponse;
use async_nats::jetstream;
use axum::{Extension, Form, Router};
use axum::extract::Query;
use axum::response::IntoResponse;
use axum::routing::get;
use serde::Deserialize;
use tokio::sync::Mutex;
use tracing::error;
use tracing_loki::url::Url;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use crate::login::{login_get, login_post};
use crate::register::{register_get, register_post};
use crate::verify::verify_get;

const VERSION: &str = env!("CARGO_PKG_VERSION");
const GIT_COMMIT: &str = env!("GIT_COMMIT");
const BUILT_ON: &str = env!("BUILT_ON");
const YEAR: &str = env!("YEAR");

#[derive(Clone)]
struct Opts {
    bind_addr: SocketAddr,
    nats_addr: SocketAddr,
    nats_cert: String,
    nats_key: String,
    asklyphe_url: String,
    loki_addr: Option<Url>,
    datacenter_id: i64,
}

#[tokio::main]
async fn main() {
    env_logger::init();
    let opts = Opts {
        bind_addr: env::var("BIND_ADDR").unwrap_or("0.0.0.0:5843".to_string()).parse().expect("Badly formed BIND_ADDR (Needs to be SocketAddr)"),
        nats_addr: env::var("NATS_ADDR").unwrap_or("127.0.0.1:4222".to_string()).to_socket_addrs().expect("Badly formed NATS_ADDR (Needs to be SocketAddr)").nth(0).unwrap(),
        nats_cert: env::var("NATS_CERT").expect("NATS_CERT needs to be set"),
        nats_key: env::var("NATS_KEY").expect("NATS_KEY needs to be set"),
        asklyphe_url: env::var("ASKLYPHE_URL").unwrap_or("https://asklyphe.com".to_string()),
        loki_addr: match env::var("LOKI_ADDR") {
            Ok(url) => {
                Some(Url::parse(&url).expect("Badly formed LOKI_ADDR (Needs to be Url)"))
            }
            Err(_) => {
                None
            }
        },
        datacenter_id: env::var("DATACENTER_ID").unwrap_or("1".to_string()).parse().expect("Badly formed DATACENTER_ID (Need to be i64)"),
    };

    //if opts.loki_addr.is_some() {
    //    let (layer, task) = tracing_loki::builder()
    //        .label("environment", "dev").unwrap()
    //        .extra_field("pid", process::id().to_string()).unwrap()
    //        .extra_field("run_id", snowflake_factory.generate().to_string()).unwrap()
    //        .build_url(opts.loki_addr.clone().unwrap()).unwrap();

    //    // Register Loki layer
    //    tracing_subscriber::registry()
    //        .with(layer)
    //        .init();

    //    // Spawn Loki background task
    //    tokio::spawn(task);
    //} else {
    //    tracing_subscriber::fmt::init()
    //}

    let nats = async_nats::ConnectOptions::new()
        .add_client_certificate(opts.nats_cert.as_str().into(), opts.nats_key.as_str().into())
        .connect(opts.nats_addr.to_string())
        .await;
    if let Err(e) = nats {
        eprintln!("FATAL ERROR, COULDN'T CONNECT TO NATS: {}", e);
        return;
    }
    let nats = nats.unwrap();
    let nats = jetstream::new(nats);
    let nats = Arc::new(Mutex::new(nats));

    let app = Router::new()
        .route("/register", get(register_get).post(register_post))
        .route("/login", get(login_get).post(login_post))
        .route("/verify", get(verify_get))
        .layer(Extension(nats.clone()))
        .layer(Extension(opts.clone()));

    let listener = tokio::net::TcpListener::bind(opts.bind_addr).await.expect("Failed to get listener");
    axum::serve(listener, app).await.expect("Failed to serve");
}

