pub mod database;
pub mod httapi;
pub mod schema;

use std::ops::Deref;
use std::str::FromStr;
use actix_web::{web, App,
                HttpResponse,
                HttpServer,
                http::header::ContentType,
                Responder,
};
use actix_cors::Cors;
use serde::Deserialize;
use database as db;
use database::DidTargetType;

#[derive(Debug, Deserialize)]
struct RouteRequest {
    #[serde(rename = "Caller-Destination-Number")]
    callee_number: String,
    #[serde(rename = "Caller-Caller-ID-Number")]
    caller_number: String,
    #[serde(rename = "Caller-Network-Addr")]
    caller_ipaddr: String
}

#[derive(Debug, Deserialize)]
struct JsonDid {
    did_number: String,
    target_type: String,
    target: String,
    trunk: String,
    active: bool
}

async fn route_did(request: web::Form<RouteRequest>) -> impl Responder {
    let callee_number = request.callee_number.clone();
    let caller_number = request.caller_number.clone();
    let caller_ipaddr = request.caller_ipaddr.clone();
    let xml = httapi::route_call(callee_number, caller_number,caller_ipaddr);
    HttpResponse::Ok()
        .content_type(ContentType::xml())
        .body(xml)
}

async fn did_post(d: web::Json<JsonDid>) -> impl Responder {
    let did = d.deref();

    println!("{:?}", did);
    db::add_did(&did.did_number, &did.target_type, &did.target, did.active ,&did.trunk).unwrap();
    HttpResponse::Ok().body("DID added.")
}

async fn did_index() -> impl Responder {
    let dids = db::list_did().unwrap();
    HttpResponse::Ok().json(dids)
}

async fn did_get(path: web::Path<i32>) ->  impl Responder {
    let id = path.into_inner();
    let did = db::get_did(id).unwrap();

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

async fn did_patch(d: web::Json<JsonDid>, path: web::Path<i32>) -> impl Responder {
    let id = path.into_inner();
    let d = d.deref();
    let did = db::Did {
        id: id,
        did_number: d.did_number.clone(),
        target_type: DidTargetType::from_str(&d.target_type).unwrap(),
        target: Some(d.target.clone()),
        trunk: d.trunk.clone(),
        active: d.active
    };

    db::update_did(did).unwrap();

    HttpResponse::Ok().body("DID updated")
}

async fn did_delete(path: web::Path<i32>) -> impl Responder {
    let id = path.into_inner();

    db::delete_did(id).unwrap();

    HttpResponse::Ok().body("DID deleted.")
}

fn api_config(cfg: &mut web::ServiceConfig) {
    cfg.service(
        web::resource("/fs")
            .route(web::get().to(did_index))
            .route(web::post().to(did_post))
    );
    cfg.service(
        web::resource("/fs/{id}")
            .route(web::get().to(did_get))
            .route(web::patch().to(did_patch))
            .route(web::delete().to(did_delete))
    );
    cfg.service(
        web::resource("/route_did")
            .route(web::post().to(route_did))
    );
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        let cors = Cors::permissive();
        App::new()
            .wrap(cors)
            .service(
                web::scope("/api").configure(api_config))
    })
        .bind(("0.0.0.0", 3000))?
        .run()
        .await
}
