use super::{
    helpers::request_heat_pump,
    models::requests::{
        ErrorResponse, HPCoolingConfig, HPDefrostConfig, HPGeneralConfig, HPHeatingConfig,
        HPWaterTankConfig, Mode, ModeResponse, StateConfigResponse,
    },
};
use crate::{
    models::SharedAppState,
    modules::heat_pump::{
        helpers::{is_temperature_in_range, is_value_valid},
        types::{HPSendingFrame, HeatPumpResponse, HeatPumpSender},
    },
};
use actix_web::{
    get, patch,
    web::{Data, Json},
    HttpResponse,
};
use log::error;

// Common responses used in mutiple requests
fn hp_timeout_res() -> HttpResponse {
    HttpResponse::GatewayTimeout().json(ErrorResponse::new("Heat pump timeout"))
}
fn hp_request_failed_res() -> HttpResponse {
    HttpResponse::InternalServerError()
        .json(ErrorResponse::new("Failed to send request to heat pump"))
}
fn hp_error_res() -> HttpResponse {
    HttpResponse::BadGateway().json(ErrorResponse::new("Heat pump error"))
}
fn hp_invalid_data_res() -> HttpResponse {
    HttpResponse::BadRequest().json(ErrorResponse::new("Invalid data"))
}
fn hp_success_res() -> HttpResponse {
    HttpResponse::Ok().finish()
}

// Every request handler in this file is prefixed with '/heatpump'
// ----------------------------------
// ------------ /heatpump ------------
// ----------------------------------

/// Get heat pump's state and configuration
#[get("/")]
pub async fn get_heat_pump(data: Data<SharedAppState>) -> HttpResponse {
    let temp_hp = data.lock().unwrap();
    HttpResponse::Ok().json(StateConfigResponse {
        config: temp_hp.hp_config,
        state: temp_hp.hp_state.clone(),
    })
}

/// Get heat pump state
#[get("/state")]
pub async fn get_heat_pump_state(data: Data<SharedAppState>) -> HttpResponse {
    let temp_hp = data.lock().unwrap();
    HttpResponse::Ok().json(temp_hp.hp_state.clone())
}

/// Get heat pump configuration
#[get("/config")]
pub async fn get_heat_pump_config(data: Data<SharedAppState>) -> HttpResponse {
    let temp_hp = data.lock().unwrap();
    HttpResponse::Ok().json(temp_hp.hp_config)
}

/// Set the heat pump mode.
/// Accepts configuration mode and returns the state mode.
#[patch("/mode")]
pub async fn update_mode(
    body: Json<Mode>,
    hp_tx: Data<HeatPumpSender>,
    app_state: Data<SharedAppState>,
) -> HttpResponse {
    if !is_value_valid(body.mode, vec![0, 1, 2, 4, 8, 16, 32, 64]) {
        return hp_invalid_data_res();
    }
    {
        let mut lock = app_state.lock().unwrap();
        lock.hp_config.mode = body.mode;
    }
    match request_heat_pump(hp_tx, HPSendingFrame::First).await {
        Ok(hp_response) => match hp_response {
            HeatPumpResponse::Success => {
                let lock = app_state.lock().unwrap();
                HttpResponse::Ok().json(ModeResponse::new(lock.hp_state.mode))
            }
            HeatPumpResponse::Timeout => hp_timeout_res(),
            HeatPumpResponse::Error => hp_error_res(),
        },
        Err(e) => {
            error!("{}", e);
            hp_request_failed_res()
        }
    }
}

// -------------------------
// ----- /general... -------
// -------------------------

/// Patch request to update heat pump general configuration
#[patch("/general")]
pub async fn update_general_configuration(
    body: Json<HPGeneralConfig>,
    hp_tx: Data<HeatPumpSender>,
    app_state: Data<SharedAppState>,
) -> HttpResponse {
    {
        let mut lock = app_state.lock().unwrap();
        if let Some(model) = body.model {
            if !is_value_valid(model, vec![1, 2, 3, 4, 5, 6, 7, 15]) {
                return hp_invalid_data_res();
            }
            lock.hp_config.model = model;
        }
        if let Some(shutdown_protection_temperature) = body.shutdown_protection_temperature {
            if !is_temperature_in_range(shutdown_protection_temperature) {
                return hp_invalid_data_res();
            }
            lock.hp_config.shutdown_protection_temperature = shutdown_protection_temperature;
        }
    }
    match request_heat_pump(hp_tx, HPSendingFrame::First).await {
        Ok(hp_response) => match hp_response {
            HeatPumpResponse::Success => hp_success_res(),
            HeatPumpResponse::Timeout => hp_timeout_res(),
            HeatPumpResponse::Error => hp_error_res(),
        },
        Err(e) => {
            error!("{}", e);
            hp_request_failed_res()
        }
    }
}

// -----------------------
// ----- /heating... -----
// -----------------------

/// Patch request to update heat pump heating configuration
#[patch("/heating")]
pub async fn update_heating_configuration(
    body: Json<HPHeatingConfig>,
    hp_tx: Data<HeatPumpSender>,
    app_state: Data<SharedAppState>,
) -> HttpResponse {
    {
        let mut lock = app_state.lock().unwrap();
        if let Some(heating_floor_temperature) = body.heating_floor_temperature {
            lock.hp_config.heating_floor_temperature = heating_floor_temperature;
        }
        if let Some(heating_hysteresis) = body.heating_hysteresis {
            lock.hp_config.heating_hysteresis = heating_hysteresis;
        }
        if let Some(room_electric_heating_mode) = body.room_electric_heating_mode {
            lock.hp_config.room_electric_heating_mode = room_electric_heating_mode;
        }
    }
    match request_heat_pump(hp_tx, HPSendingFrame::First).await {
        Ok(hp_response) => match hp_response {
            HeatPumpResponse::Success => hp_success_res(),
            HeatPumpResponse::Timeout => hp_timeout_res(),
            HeatPumpResponse::Error => hp_error_res(),
        },
        Err(e) => {
            error!("{}", e);
            hp_request_failed_res()
        }
    }
}

// --------------------------
// ----- /defrosting... -----
// --------------------------

/// Patch request to update heat pump defrost configuration
#[patch("/defrosting")]
pub async fn update_defrosting_configuration(
    body: Json<HPDefrostConfig>,
    hp_tx: Data<HeatPumpSender>,
    app_state: Data<SharedAppState>,
) -> HttpResponse {
    {
        let mut lock = app_state.lock().unwrap();
        if let Some(defrost_mode) = body.defrost_mode {
            if !is_value_valid(defrost_mode, vec![0, 1]) {
                return hp_invalid_data_res();
            }
            lock.hp_config.defrost_mode = defrost_mode;
        }
        if let Some(defrost_into_temperature) = body.defrost_into_temperature {
            lock.hp_config.defrost_into_temperature = defrost_into_temperature;
        }
        if let Some(defrost_exit_temperature) = body.defrost_exit_temperature {
            lock.hp_config.defrost_exit_temperature = defrost_exit_temperature;
        }
        if let Some(defrost_maximum_time) = body.defrost_maximum_time {
            lock.hp_config.defrost_maximum_time = defrost_maximum_time;
        }
        if let Some(defrost_entry_cycle_time) = body.defrost_entry_cycle_time {
            lock.hp_config.defrost_entry_cycle_time = defrost_entry_cycle_time;
        }
    }
    match request_heat_pump(hp_tx, HPSendingFrame::Second).await {
        Ok(hp_response) => match hp_response {
            HeatPumpResponse::Success => hp_success_res(),
            HeatPumpResponse::Timeout => hp_timeout_res(),
            HeatPumpResponse::Error => hp_error_res(),
        },
        Err(e) => {
            error!("{}", e);
            hp_request_failed_res()
        }
    }
}

// -------------------------
// ----- /watertank... -----
// -------------------------

/// Patch request to update heat pump water tank configuration
#[patch("/watertank")]
pub async fn update_water_tank_configuration(
    body: Json<HPWaterTankConfig>,
    hp_tx: Data<HeatPumpSender>,
    app_state: Data<SharedAppState>,
) -> HttpResponse {
    {
        let mut lock = app_state.lock().unwrap();
        if let Some(water_tank_temperature) = body.water_tank_temperature {
            lock.hp_config.water_tank_temperature = water_tank_temperature;
        }
        if let Some(water_tank_hysteresis) = body.water_tank_hysteresis {
            lock.hp_config.water_tank_hysteresis = water_tank_hysteresis;
        }
    }
    match request_heat_pump(hp_tx, HPSendingFrame::Second).await {
        Ok(hp_response) => match hp_response {
            HeatPumpResponse::Success => hp_success_res(),
            HeatPumpResponse::Timeout => hp_timeout_res(),
            HeatPumpResponse::Error => hp_error_res(),
        },
        Err(e) => {
            error!("{}", e);
            hp_request_failed_res()
        }
    }
}

// -------------------------
// ----- /cooling... -------
// -------------------------

/// Patch request to update heat pump water tank configuration
#[patch("/cooling")]
pub async fn update_cooling_configuration(
    body: Json<HPCoolingConfig>,
    hp_tx: Data<HeatPumpSender>,
    app_state: Data<SharedAppState>,
) -> HttpResponse {
    {
        let mut lock = app_state.lock().unwrap();
        if let Some(cooling_temperature) = body.cooling_temperature {
            lock.hp_config.cooling_temperature = cooling_temperature;
        }
    }
    match request_heat_pump(hp_tx, HPSendingFrame::Second).await {
        Ok(hp_response) => match hp_response {
            HeatPumpResponse::Success => hp_success_res(),
            HeatPumpResponse::Timeout => hp_timeout_res(),
            HeatPumpResponse::Error => hp_error_res(),
        },
        Err(e) => {
            error!("{}", e);
            hp_request_failed_res()
        }
    }
}
