use crate::cmd::config::{resolve_config, ConfigArgsRef};
use crate::cmd::CliContext;
use axum::async_trait;
use clap::Parser;
use eternaltwin_core::core::Duration;
use eternaltwin_core::hammerfest::HammerfestClientRef;
use eternaltwin_core::job::{Sleep, Task, TaskCx, TaskPoll};
use eternaltwin_core::types::WeakError;
use eternaltwin_system::EternaltwinSystem;
use serde::{Deserialize, Serialize};

/// Arguments to the `job` task.
#[derive(Debug, Parser)]
pub struct Args {
  #[clap(flatten)]
  config: crate::cmd::config::Args,
}

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)?;

  let rt = &*system.job_runtime;

  // let job = ScrapeAllHammerfestProfiles::Start {
  //   credentials: HammerfestCredentials {
  //     server: HammerfestServer::HammerfestFr,
  //     username: "".parse().unwrap(),
  //     password: "".parse().unwrap(),
  //   },
  // };
  // rt.spawn(job, None).await.unwrap();
  dbg!(system.clock.now());
  loop {
    // TODO: Add a version of `wait_for_available` that does not wait on new jobs
    dbg!(("tick", system.clock.now()));
    rt.tick().await.unwrap();
    rt.wait_for_available().await.unwrap();
  }
  // // let out = rt.try_join(handle).await.unwrap();
  // dbg!(api.clock.now());
  // // dbg!(out);
  // // assert_eq!(out, 5);
  // Ok(())
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Adder {
  Start,
  First(Sleep),
  Second(u32, Sleep),
  Ready(u32),
}

#[async_trait]
impl<'cx, Cx> Task<&'cx mut Cx> for Adder
where
  Cx: TaskCx + HammerfestClientRef,
{
  const NAME: &'static str = "Adder";
  const VERSION: u32 = 1;
  type Output = u32;

  async fn poll(&mut self, cx: &'cx mut Cx) -> TaskPoll<Self::Output> {
    loop {
      match self {
        Self::Start => {
          dbg!("Start");
          *self = Self::First(cx.sleep(Duration::from_seconds(2)));
        }
        Self::First(sleep) => {
          dbg!("First");
          let first = match sleep.poll(cx).await {
            TaskPoll::Ready(()) => 2,
            TaskPoll::Pending => return TaskPoll::Pending,
          };
          *self = Self::Second(first, cx.sleep(Duration::from_seconds(2)));
        }
        Self::Second(first, sleep) => {
          dbg!("Second");
          let second = match sleep.poll(cx).await {
            TaskPoll::Ready(()) => 3,
            TaskPoll::Pending => return TaskPoll::Pending,
          };
          *self = Self::Ready(*first + second);
        }
        Self::Ready(result) => {
          dbg!("Ready");
          return TaskPoll::Ready(*result);
        }
      }
    }
  }
}
