use crate::devices::device::{DeviceId, DeviceRelay, DeviceRelayID};
use crate::energy::energy_actor;
use crate::server::WebEnergyActor;
use crate::server::custom_error::HttpResult;
use actix_web::{HttpResponse, web};

/// Get the full list of relays
pub async fn get_list(actor: WebEnergyActor) -> HttpResult {
    let list = actor.send(energy_actor::GetRelaysList).await?;
    Ok(HttpResponse::Ok().json(list))
}

#[derive(serde::Deserialize)]
pub struct CreateDeviceRelayRequest {
    device_id: DeviceId,
    #[serde(flatten)]
    relay: DeviceRelay,
}

/// Create a new relay
pub async fn create(actor: WebEnergyActor, req: web::Json<CreateDeviceRelayRequest>) -> HttpResult {
    let list = actor.send(energy_actor::GetRelaysList).await?;

    if let Some(e) = req.relay.error(&list) {
        log::error!("Invalid relay create query: {e}");
        return Ok(HttpResponse::BadRequest().json(e));
    }

    let Some(device) = actor
        .send(energy_actor::GetSingleDevice(req.device_id.clone()))
        .await?
    else {
        log::error!("Invalid relay create query: specified device does not exists!");
        return Ok(HttpResponse::NotFound().json("Linked device not found!"));
    };

    if device.relays.len() >= device.info.max_relays {
        log::error!("Invalid relay create query: too many relay for the target device!");
        return Ok(HttpResponse::BadRequest().json("Too many relays for the target device!"));
    }

    if actor
        .send(energy_actor::GetSingleRelay(req.relay.id))
        .await?
        .is_some()
    {
        log::error!("Invalid relay create query: A relay with the same ID already exists!");
        return Ok(HttpResponse::BadRequest().json("A relay with the same ID already exists!"));
    }

    // Create the device relay
    actor
        .send(energy_actor::CreateDeviceRelay(
            req.device_id.clone(),
            req.relay.clone(),
        ))
        .await??;

    Ok(HttpResponse::Accepted().finish())
}

#[derive(serde::Deserialize)]
pub struct RelayIDInPath {
    id: DeviceRelayID,
}

/// Update a relay configuration
pub async fn update(
    actor: WebEnergyActor,
    mut req: web::Json<DeviceRelay>,
    path: web::Path<RelayIDInPath>,
) -> HttpResult {
    req.id = path.id;

    let list = actor.send(energy_actor::GetRelaysList).await?;

    if let Some(e) = req.error(&list) {
        log::error!("Invalid relay update query: {e}");
        return Ok(HttpResponse::BadRequest().json(e));
    }

    // Create the device relay
    actor.send(energy_actor::UpdateDeviceRelay(req.0)).await??;

    Ok(HttpResponse::Accepted().finish())
}

/// Delete an existing relay
pub async fn delete(actor: WebEnergyActor, path: web::Path<RelayIDInPath>) -> HttpResult {
    actor
        .send(energy_actor::DeleteDeviceRelay(path.id))
        .await??;

    Ok(HttpResponse::Accepted().finish())
}

/// Get the status of all relays
pub async fn status_all(actor: WebEnergyActor) -> HttpResult {
    let list = actor.send(energy_actor::GetAllRelaysState).await?;

    Ok(HttpResponse::Ok().json(list))
}

/// Get the state of a single relay
pub async fn status_single(actor: WebEnergyActor, path: web::Path<RelayIDInPath>) -> HttpResult {
    let list = actor.send(energy_actor::GetAllRelaysState).await?;
    let Some(state) = list.into_iter().find(|r| r.id == path.id) else {
        return Ok(HttpResponse::NotFound().json("Relay not found!"));
    };

    Ok(HttpResponse::Ok().json(state))
}