use crate::cmd::config::{populate_system, resolve_config, ConfigArgsRef};
use crate::cmd::CliContext;
use crate::output::OutFormat;
use clap::Parser;
use eternaltwin_core::types::{DisplayErrorChainExt, WeakError};
use eternaltwin_system::EternaltwinSystem;
use std::future::IntoFuture;
use std::net::SocketAddr;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
use tokio::net::TcpListener;

/// Arguments to the `backend` task.
#[derive(Debug, Parser)]
pub struct Args {
  #[clap(flatten)]
  config: crate::cmd::config::Args,
  #[arg(long, value_enum, default_value_t)]
  format: OutFormat,
}

impl ConfigArgsRef for Args {
  fn config_args(&self) -> &crate::cmd::config::Args {
    &self.config
  }
}

pub async fn run(cx: CliContext<'_, Args>) -> Result<(), WeakError> {
  let config = resolve_config(&cx, true).await?;
  let system = EternaltwinSystem::create_dyn(&config, cx.out)
    .await
    .map_err(WeakError::wrap)?;

  eprintln!("loaded internal Eternaltwin system");

  populate_system(&system, &config, true).await?;

  eprintln!("initialization complete");

  let mut listen_addr: SocketAddr = match SocketAddr::from_str(&config.backend.listen.value) {
    Ok(addr) => addr,
    Err(e) => {
      eprintln!("invalid listen address {}", &config.backend.listen.value);
      return Err(WeakError::wrap(e));
    }
  };
  if let Some(port) = config.backend.port.value {
    listen_addr.set_port(port);
  }

  eprintln!("started server at http://localhost:{}/", listen_addr.port());

  let job_rt = {
    let job_rt = Arc::clone(&system.job_runtime);
    async move {
      loop {
        if let Err(e) = job_rt.tick().await {
          eprintln!("job_rt.tick error: {}", e.display_chain());
          eprintln!("job_runtime tick error, sleep for 10min");
          tokio::time::sleep(Duration::from_secs(600)).await;
        }
        if let Err(e) = job_rt.wait_for_available().await {
          eprintln!("job_rt.tick error {}", e.display_chain());
          eprintln!("job_runtime tick error, sleep for 10min");
          tokio::time::sleep(Duration::from_secs(600)).await;
        };
      }
    }
  };

  let listener = TcpListener::bind(listen_addr).await.unwrap();
  let server = axum::serve(listener, eternaltwin_rest::app(system)).into_future();

  tokio::select! {
    _ = server => {
      panic!("server crash");
    }
    _ = job_rt => {
      panic!("job runtime crash");
    }
  }
}
