use crate::config::Config;
use crate::ipc::{JobStatus, Request, Response};
use crate::scheduler;
use crate::state::{self, JobState, SchedulerState};

pub async fn run(config: Config) {
	let state = state::new_state();
	scheduler::load_jobs(&config, &state).await;

	let scheduler_state = state.clone();
	tokio::spawn(async move {
		scheduler::run_scheduler(scheduler_state).await;
	});

	let daemon = muzan::Daemon::new("koku");
	let handler_state = state.clone();
	let config_path = Config::default_path();

	daemon
		.run(move |req: Request| {
			let state = handler_state.clone();
			let config_path = config_path.clone();
			async move { handle_request(req, &state, &config_path).await }
		})
		.await;
}

async fn handle_request(
	req: Request,
	state: &SchedulerState,
	config_path: &std::path::Path,
) -> Response {
	match req {
		Request::Status => {
			let jobs = state.read().await;
			let mut statuses: Vec<JobStatus> = jobs
				.values()
				.map(|rt| {
					let fmt = |dt: &chrono::DateTime<chrono::Local>| {
						dt.format("%Y-%m-%d %H:%M:%S").to_string()
					};
					JobStatus {
						name: rt.config.name.clone(),
						state: rt.state,
						last_run: rt.last_run.as_ref().map(fmt),
						last_exit: rt.last_exit,
						next_run: rt.next_run.as_ref().map(fmt),
					}
				})
				.collect();
			statuses.sort_by(|a, b| a.name.cmp(&b.name));
			Response::Status(statuses)
		}

		Request::Run(name) => {
			let exists = {
				let jobs = state.read().await;
				jobs.contains_key(&name)
			};
			if !exists {
				return Response::Error(format!("unknown job: {}", name));
			}
			let state = state.clone();
			let name_clone = name.clone();
			tokio::spawn(async move {
				crate::scheduler::execute_job_by_name(&name_clone, &state).await;
			});
			Response::Ok(format!("triggered: {}", name))
		}

		Request::Pause(name) => {
			let mut jobs = state.write().await;
			if let Some(rt) = jobs.get_mut(&name) {
				rt.state = JobState::Paused;
				rt.next_run = None;
				Response::Ok(format!("paused: {}", name))
			} else {
				Response::Error(format!("unknown job: {}", name))
			}
		}

		Request::Resume(name) => {
			let mut jobs = state.write().await;
			if let Some(rt) = jobs.get_mut(&name) {
				rt.state = JobState::Idle;
				rt.consecutive_failures = 0;
				rt.backoff_until = None;
				if let Some(next) = crate::scheduler::next_run_for(&rt.config.cron) {
					rt.next_run = Some(next);
				}
				Response::Ok(format!("resumed: {}", name))
			} else {
				Response::Error(format!("unknown job: {}", name))
			}
		}

		Request::Reload => {
			match Config::load(&config_path.to_path_buf()) {
				Ok(config) => {
					let errors = config.validate();
					if !errors.is_empty() {
						return Response::Error(format!("config errors: {}", errors.join(", ")));
					}
					scheduler::reload_jobs(&config, state).await;
					Response::Ok("reloaded".to_string())
				}
				Err(e) => Response::Error(e),
			}
		}
	}
}
