mod auth_guard;
mod business_metrics;
mod k8s;
mod routes;
mod state;
mod stripe_util;

use std::sync::Arc;

use axum::{Extension, Router, routing::{get, post}};
use tower_http::services::ServeDir;
use openid::DiscoveredClient;
use state::AppState;
use tower_sessions::{MemoryStore, SessionManagerLayer, cookie::SameSite};
use irc_now_common::auth::OidcConfig;
use irc_now_common::db::DbConfig;

async fn health() -> &'static str {
    "ok"
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .init();

    let db = DbConfig::from_env("ACCOUNTS").connect().await.unwrap();
    let oidc = OidcConfig::from_env();

    let issuer_url = url::Url::parse(&oidc.issuer_url).expect("invalid OIDC issuer URL");

    tracing::info!("discovering OIDC provider at {issuer_url}");
    let oidc_client = DiscoveredClient::discover(
        oidc.client_id.clone(),
        oidc.client_secret.clone(),
        Some(oidc.redirect_url.clone()),
        issuer_url,
    )
    .await
    .expect("OIDC discovery failed");

    tracing::info!("OIDC provider discovered");

    let stripe_webhook_secret =
        std::env::var("STRIPE_WEBHOOK_SECRET").unwrap_or_default();
    let stripe_secret_key =
        std::env::var("STRIPE_SECRET_KEY").unwrap_or_default();
    let stripe_price_id =
        std::env::var("STRIPE_PRICE_ID").unwrap_or_default();
    let stripe_client = stripe::Client::new(&stripe_secret_key);

    let kube_client = kube::Client::try_default()
        .await
        .expect("kube client init failed");
    let namespace =
        std::env::var("NAMESPACE").unwrap_or_else(|_| "irc-josie-cloud".to_string());

    let http_client = reqwest::Client::new();

    let metrics_db = db.clone();
    let metrics_kube = kube_client.clone();
    let metrics_namespace = namespace.clone();

    let state = AppState {
        db,
        oidc,
        oidc_client: Arc::new(oidc_client),
        stripe_webhook_secret,
        stripe_client,
        stripe_price_id,
        kube: kube_client,
        namespace,
        http_client,
    };

    tokio::spawn(business_metrics::record_user_metrics(metrics_db));
    tokio::spawn(business_metrics::record_soju_metrics(metrics_kube, metrics_namespace));

    let metrics_handle = irc_now_common::metrics::setup_metrics_recorder();

    let session_store = MemoryStore::default();
    let session_layer = SessionManagerLayer::new(session_store).with_same_site(SameSite::Lax);

    let app = Router::new()
        .route("/health", get(health))
        .route("/metrics", get(irc_now_common::metrics::metrics_handler))
        .route("/auth/login", get(routes::auth::login))
        .route("/auth/callback", get(routes::auth::callback))
        .route("/auth/logout", get(routes::auth::logout))
        .route("/dashboard", get(routes::dashboard::index))
        .route("/bouncers", get(routes::bouncer::list))
        .route("/bouncers/create", post(routes::bouncer::create))
        .route("/billing", get(routes::billing::index))
        .route("/billing/checkout", post(routes::billing::checkout))
        .route("/billing/portal", get(routes::billing::portal))
        .route("/billing/webhook", post(routes::billing::webhook))
        .route("/profile", get(routes::profile::index))
        .route("/profile/update", post(routes::profile::update))
        .nest_service("/static", ServeDir::new("static"))
        .layer(session_layer)
        .layer(axum::middleware::from_fn(irc_now_common::metrics::track_metrics))
        .layer(Extension(metrics_handle))
        .with_state(state);

    let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();
    tracing::info!("listening on {}", listener.local_addr().unwrap());
    axum::serve(listener, app).await.unwrap();
}
