use std::sync::Arc;

use futures::StreamExt;
use k8s_crds_cert_manager::certificates::Certificate;
use k8s_openapi::api::apps::v1::Deployment;
use k8s_openapi::api::core::v1::{ConfigMap, Secret, Service};
use kube::api::Api;
use kube::runtime::controller::Controller;
use kube::runtime::watcher;
use kube::Client;

use soju_operator::controller::{self, Context};
use soju_operator::SojuBouncer;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter(
            tracing_subscriber::EnvFilter::from_default_env()
                .add_directive("soju_operator=info".parse()?),
        )
        .init();

    let client = Client::try_default().await?;

    let db_master_uri =
        std::env::var("SOJU_DB_MASTER_URI").expect("SOJU_DB_MASTER_URI must be set");
    let db_host = std::env::var("SOJU_DB_HOST").expect("SOJU_DB_HOST must be set");
    let db_port: u16 = std::env::var("SOJU_DB_PORT")
        .unwrap_or_else(|_| "5432".to_string())
        .parse()?;

    let db_config: deadpool_postgres::Config = db_master_uri.parse()
        .unwrap_or_else(|_| {
            let mut cfg = deadpool_postgres::Config::new();
            cfg.url = Some(db_master_uri);
            cfg
        });
    let db_pool = db_config
        .create_pool(Some(deadpool_postgres::Runtime::Tokio1), tokio_postgres::NoTls)?;

    let ctx = Arc::new(Context {
        client: client.clone(),
        db_host,
        db_port,
        db_pool,
    });

    let bouncers = Api::<SojuBouncer>::all(client.clone());
    let deployments = Api::<Deployment>::all(client.clone());
    let configmaps = Api::<ConfigMap>::all(client.clone());
    let services = Api::<Service>::all(client.clone());
    let secrets = Api::<Secret>::all(client.clone());

    tracing::info!("starting soju-operator");

    Controller::new(bouncers, watcher::Config::default())
        .owns(deployments, watcher::Config::default())
        .owns(configmaps, watcher::Config::default())
        .owns(services, watcher::Config::default())
        .owns(secrets, watcher::Config::default())
        .shutdown_on_signal()
        .run(controller::reconcile, controller::error_policy, ctx)
        .for_each(|res| async move {
            match res {
                Ok(o) => tracing::info!("reconciled {:?}", o),
                Err(e) => tracing::warn!("reconcile failed: {:?}", e),
            }
        })
        .await;

    Ok(())
}
