// SPDX-FileCopyrightText: Politik im Blick developers
// SPDX-FileCopyrightText: Wolfgang Silbermayr <wolfgang@silbermayr.at>
//
// SPDX-License-Identifier: AGPL-3.0-or-later OR EUPL-1.2

use std::sync::Arc;

use dirs::data_local_dir;
use oparl_service_facade::OParlServiceHandle;
use oparl_types::DateTime;
use pib_oparl_service::PibOParlService;
use pib_service_api_types::config::oidc::OidcConfig;
use pib_service_db_migration::Migrator;
use pib_service_inventory::InventoryProvider;
use pib_service_inventory_db::DatabaseInventoryProvider;
use sea_orm::Database;
use sea_orm_migration::MigratorTrait as _;
use tokio::{select, sync::RwLock};

mod settings;

use crate::settings::{ApiOidcSettings, ApiSettings, Settings};

const PROGRAM_NAME: &str = env!("CARGO_PKG_NAME");

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    env_logger::init();

    let settings = Settings::load()?;

    let data_dir = data_local_dir().unwrap().join("pib-service");
    std::fs::create_dir_all(&data_dir)?;
    let db_path = data_dir.join("inventory.sqlite?mode=rwc");
    let db_url = format!("sqlite:{}", db_path.to_str().unwrap());
    let db = Database::connect(db_url).await?;
    Migrator::up(&db, None).await.unwrap();

    let name = Some(PROGRAM_NAME.into());
    let vendor = Some("https://codeberg.org/pib".parse()?);
    let product = Some("https://codeberg.org/pib/pib-service".parse()?);
    let now = DateTime::now();
    let created = now.clone();
    let modified = now.clone();
    let pib_about = Some(
        "An experimental OParl service developed by the \"Politik im Blick\" developers".into(),
    );

    let database_inventory_provider: Arc<dyn InventoryProvider> = Arc::new(
        DatabaseInventoryProvider::new(db, name, vendor, product, created, modified, pib_about),
    );

    let service = OParlServiceHandle::new(Arc::new(RwLock::new(PibOParlService::new(
        settings.oparl.base_url,
        database_inventory_provider.clone(),
    ))));
    let service_future = oparl_web_service_axum::run(service, settings.http.bind.as_slice());

    if let Some(ApiSettings { oidc, http }) = settings.api.as_ref() {
        let ApiOidcSettings {
            issuer,
            client_id,
            client_secret,
        } = oidc.clone();
        let oidc = OidcConfig {
            issuer,
            client_id,
            client_secret,
        };
        let service_api_future =
            pib_service_api::run(database_inventory_provider, http.bind.as_slice(), oidc);

        select! {
            result = service_future => result?,
            result = service_api_future => result?
        }
    } else {
        service_future.await?;
    }

    Ok(())
}
