use std::env;

use super::{evok_handlers as evok, heat_pump_handlers as hp};
use crate::{
    modules::{evok::types::EvokSender, heat_pump::types::HeatPumpSender},
    APP_STATE,
};
use actix_web::{
    middleware::{DefaultHeaders, Logger},
    web::{self, Data},
    App, HttpServer,
};

// flow for each handler:
// 1. validate data from the request
// 2. update configuration
// 3. send a message to corresponding service (channel)
// 4. service do some action
// 5. service oneshot message to notify the API that the action is done
// 6. if the action was successful return success response
//    with optional state data (eg. mode (as config mode and state mode can differ))
// 7. return a response

pub async fn run(evok_tx: EvokSender, hp_tx: HeatPumpSender) {
    let ip_address: String =
        env::var("API_IP_ADDRESS").expect("Environment variable API_IP_ADDRESS not found");
    let port: u16 = env::var("API_PORT")
        .expect("Environment variable API_PORT not found")
        .parse()
        .expect("Environment variable API_PORT has invalid type (u16 is required)");

    if ip_address.is_empty() {
        panic!("Environment variable API_IP_ADDRESS is empty");
    }

    let _ = HttpServer::new(move || {
        App::new()
            .app_data(Data::new(APP_STATE.clone()))
            .app_data(Data::new(hp_tx.clone()))
            .app_data(Data::new(evok_tx.clone()))
            .wrap(Logger::default())
            .wrap(DefaultHeaders::new().add(("content-type", "application/json")))
            .service(
                web::scope("/evok")
                    .service(evok::get_evok_state)
                    .service(evok::set_relay),
            )
            .service(
                web::scope("/heatpump")
                    .service(hp::get_heat_pump)
                    .service(hp::get_heat_pump_state)
                    .service(hp::get_heat_pump_config)
                    .service(hp::update_mode)
                    .service(hp::update_general_configuration)
                    .service(hp::update_heating_configuration)
                    .service(hp::update_defrosting_configuration)
                    .service(hp::update_cooling_configuration)
                    .service(hp::update_water_tank_configuration),
            )
    })
    .bind((ip_address, port))
    .unwrap()
    .run()
    .await;
}
