use actix::{Actor, AsyncContext};
use emgauwa_common::db::{DbController, DbJunctionRelaySchedule, DbRelay, DbSchedule};
use emgauwa_common::errors::{DatabaseError, EmgauwaError};
use emgauwa_common::models::{Controller, FromDbModel};
use emgauwa_common::types::{ControllerWsAction, EmgauwaUid, RelayStates};
use emgauwa_common::utils;
use futures::executor::block_on;
use sqlx::pool::PoolConnection;
use sqlx::Sqlite;

use crate::app_state::{Action, ConnectController, UpdateRelayStates};
use crate::handlers::v1::ws::controllers::ControllersWs;

impl ControllersWs {
	pub fn handle_register(
		&mut self,
		conn: &mut PoolConnection<Sqlite>,
		ctx: &mut <ControllersWs as Actor>::Context,
		controller: Controller,
	) -> Result<(), EmgauwaError> {
		log::info!(
			"Registering controller: {} ({})",
			controller.c.name,
			controller.c.uid
		);
		let c = &controller.c;
		let controller_db = block_on(DbController::get_by_uid_or_create(
			conn,
			&c.uid,
			&c.name,
			c.relay_count,
		))?;
		block_on(controller_db.update_active(conn, true))?;
		// update only the relay count
		block_on(controller_db.update(conn, &controller_db.name, c.relay_count))?;

		for relay in &controller.relays {
			log::debug!(
				"Registering relay: {} ({})",
				relay.r.name,
				match relay.is_on {
					Some(true) => "+",
					Some(false) => "-",
					None => "?",
				}
			);
			let (new_relay, created) = block_on(DbRelay::get_by_controller_and_num_or_create(
				conn,
				&controller_db,
				relay.r.number,
				&relay.r.name,
			))?;
			if created {
				let mut relay_schedules = Vec::new();
				for schedule in &relay.schedules {
					let (new_schedule, _) = block_on(DbSchedule::get_by_uid_or_create(
						conn,
						schedule.uid.clone(),
						&schedule.name,
						&schedule.periods,
					))?;
					relay_schedules.push(new_schedule);
				}

				block_on(DbJunctionRelaySchedule::set_schedules(
					conn,
					&new_relay,
					relay_schedules.iter().collect(),
				))?;
			}
		}

		let controller_uid = &controller.c.uid;
		let controller_db = block_on(DbController::get_by_uid(conn, controller_uid))?
			.ok_or(DatabaseError::InsertGetError)?;
		let controller = Controller::from_db_model(conn, controller_db)?;

		let addr = ctx.address();
		self.controller_uid = Some(controller_uid.clone());
		block_on(self.app_state.send(ConnectController {
			address: addr.recipient(),
			controller: controller.clone(),
		}))??;

		block_on(self.app_state.send(Action {
			controller_uid: controller_uid.clone(),
			action: ControllerWsAction::Controller(controller.clone()),
		}))??;
		block_on(self.app_state.send(Action {
			controller_uid: controller_uid.clone(),
			action: ControllerWsAction::Relays(controller.relays),
		}))??;

		log::debug!("Done registering controller");
		Ok(())
	}

	pub fn handle_relay_states(
		&mut self,
		controller_uid: EmgauwaUid,
		relay_states: RelayStates,
	) -> Result<(), EmgauwaError> {
		log::debug!(
			"Received relay states: {} for {}",
			utils::printable_relay_states(&relay_states),
			controller_uid
		);
		block_on(self.app_state.send(UpdateRelayStates {
			controller_uid,
			relay_states,
		}))?;
		Ok(())
	}
}