use std::ops::DerefMut;

use sqlx::pool::PoolConnection;
use sqlx::Sqlite;

use crate::db::{DbMacro, DbRelay, DbSchedule};
use crate::errors::DatabaseError;

#[derive(Debug, Clone)]
pub struct DbMacroAction {
	pub id: i64,
	pub macro_id: i64,
	pub relay_id: i64,
	pub schedule_id: i64,
	pub weekday: i64, // should be u8, but sqlite will store it as i64
}


impl DbMacroAction {
	pub async fn get_all(
		conn: &mut PoolConnection<Sqlite>,
	) -> Result<Vec<DbMacroAction>, DatabaseError> {
		sqlx::query_as!(DbMacroAction, "SELECT * FROM macro_actions")
			.fetch_all(conn.deref_mut())
			.await
			.map_err(DatabaseError::from)
	}

	pub async fn get(
		conn: &mut PoolConnection<Sqlite>,
		id: i64,
	) -> Result<Option<DbMacroAction>, DatabaseError> {
		sqlx::query_as!(
			DbMacroAction,
			"SELECT * FROM macro_actions WHERE id = ?",
			id
		)
		.fetch_optional(conn.deref_mut())
		.await
		.map_err(DatabaseError::from)
	}

	pub async fn create(
		conn: &mut PoolConnection<Sqlite>,
		new_macro: &DbMacro,
		new_relay: &DbRelay,
		new_schedule: &DbSchedule,
		new_weekday: i64,
	) -> Result<DbMacroAction, DatabaseError> {
		sqlx::query_as!(
			DbMacroAction,
			"INSERT INTO macro_actions (macro_id, relay_id, schedule_id, weekday) VALUES (?, ?, ?, ?) RETURNING *",
			new_macro.id,
			new_relay.id,
			new_schedule.id,
			new_weekday
		)
		.fetch_optional(conn.deref_mut())
		.await?
		.ok_or(DatabaseError::InsertGetError)
	}

	pub async fn delete(&self, conn: &mut PoolConnection<Sqlite>) -> Result<(), DatabaseError> {
		sqlx::query!("DELETE FROM macro_actions WHERE id = ?", self.id)
			.execute(conn.deref_mut())
			.await
			.map(|res| match res.rows_affected() {
				0 => Err(DatabaseError::DeleteError),
				_ => Ok(()),
			})?
	}

	pub async fn get_relay(
		&self,
		conn: &mut PoolConnection<Sqlite>,
	) -> Result<DbRelay, DatabaseError> {
		DbRelay::get(conn, self.relay_id)
			.await?
			.ok_or(DatabaseError::NotFound)
	}

	pub async fn get_schedule(
		&self,
		conn: &mut PoolConnection<Sqlite>,
	) -> Result<DbSchedule, DatabaseError> {
		DbSchedule::get(conn, self.schedule_id)
			.await?
			.ok_or(DatabaseError::NotFound)
	}

	pub async fn get_macro(
		&self,
		conn: &mut PoolConnection<Sqlite>,
	) -> Result<DbMacro, DatabaseError> {
		DbMacro::get(conn, self.macro_id)
			.await?
			.ok_or(DatabaseError::NotFound)
	}
}