use axum::extract::{Query, State};
use axum::http::StatusCode;
use axum::response::Response;
use axum::routing::post;
use axum::Router;
use aws_local_core::db::DbPool;
use aws_local_core::errors::aws_error_response;
use serde::Deserialize;
use std::env;
use std::sync::Arc;

use crate::{db, docker::DockerManager, operations};

#[derive(Clone)]
pub struct AppState {
    pub db: DbPool,
    pub docker: DockerManager,
    pub drop_on_delete: bool,
}

#[derive(Deserialize)]
pub struct ActionQuery {
    #[serde(rename = "Action")]
    pub action: Option<String>,
}

pub fn build_router(state: AppState) -> Router {
    Router::new()
        .route("/", post(dispatch_handler))
        .with_state(Arc::new(state))
}

pub fn new_state(data_dir: &str) -> AppState {
    let pool = db::connect(data_dir).expect("failed to open rds.db");
    db::run_migrations(&pool).expect("failed to run RDS migrations");
    let docker = DockerManager::new().expect("failed to connect to Docker");
    let drop_on_delete = env::var("RDS_DROP_ON_DELETE")
        .map(|v| v != "false" && v != "0")
        .unwrap_or(true);
    AppState { db: pool, docker, drop_on_delete }
}

async fn dispatch_handler(
    State(state): State<Arc<AppState>>,
    Query(query): Query<ActionQuery>,
    body: String,
) -> Response {
    let action = match query.action {
        Some(a) => a,
        None => return aws_error_response(StatusCode::BAD_REQUEST, "MissingAction", "Action query param required"),
    };
    let json_body: serde_json::Value = if body.is_empty() {
        serde_json::Value::Object(Default::default())
    } else {
        serde_json::from_str(&body).unwrap_or(serde_json::Value::Object(Default::default()))
    };
    operations::dispatch(&action, json_body, &state.db, &state.docker, state.drop_on_delete).await
}
