use axum::Json;
use axum::extract::{Path, Query, State};
use axum::http::StatusCode;
use serde::Deserialize;
use serde_json::{Value, json};
use super::stations::load;
use crate::calls;
use crate::db;
use crate::error::AppError;
use crate::state::App;
use crate::tariff;
pub async fn put(
State(app): State<App>,
Path(tariff_id): Path<String>,
Json(mut body): Json<Value>,
) -> Result<(StatusCode, Json<Value>), AppError> {
match body.get("tariffId").and_then(Value::as_str) {
None => body["tariffId"] = json!(tariff_id),
Some(id) if id != tariff_id => {
return Err(AppError::BadRequest(
"tariffId in the body differs from the path".into(),
));
}
Some(_) => {}
}
let t = tariff::decode(&body).map_err(AppError::BadRequest)?;
tariff::validate(&t).map_err(AppError::BadRequest)?;
let canonical = serde_json::to_value(&t).map_err(|e| AppError::Internal(e.to_string()))?;
match db::tariffs::upsert(&app.db, &tariff_id, &t.currency, &canonical).await? {
db::tariffs::Upsert::Created(row) => Ok((StatusCode::CREATED, Json(json!(row)))),
db::tariffs::Upsert::Unchanged(row) => Ok((StatusCode::OK, Json(json!(row)))),
db::tariffs::Upsert::Conflict => Err(AppError::Conflict(format!(
"tariff {tariff_id} exists with different content; use a new tariffId (I08.FR.08)"
))),
}
}
pub async fn list(State(app): State<App>) -> Result<Json<Value>, AppError> {
Ok(Json(json!(db::tariffs::list(&app.db).await?)))
}
pub async fn get(
State(app): State<App>,
Path(tariff_id): Path<String>,
) -> Result<Json<Value>, AppError> {
let row = db::tariffs::get(&app.db, &tariff_id)
.await?
.ok_or_else(|| AppError::NotFound(format!("tariff {tariff_id} not found")))?;
let t = tariff::decode(&row.tariff).map_err(AppError::Internal)?;
Ok(Json(json!({ "tariff": row, "text": tariff::text(&t) })))
}
pub async fn delete(
State(app): State<App>,
Path(tariff_id): Path<String>,
) -> Result<StatusCode, AppError> {
if db::tariffs::delete(&app.db, &tariff_id).await? {
Ok(StatusCode::NO_CONTENT)
} else {
Err(AppError::NotFound(format!("tariff {tariff_id} not found")))
}
}
#[derive(Debug, Deserialize)]
pub struct StationTariffQuery {
pub source: Option<String>,
pub evse_id: Option<i64>,
}
pub async fn station_list(
State(app): State<App>,
Path(identity): Path<String>,
Query(q): Query<StationTariffQuery>,
) -> Result<Json<Value>, AppError> {
if q.source.as_deref() == Some("station") {
let version = calls::connected_version(&app, &identity).await?;
let response = calls::call(
&app,
&identity,
version,
"GetTariffs",
json!({ "evseId": q.evse_id.unwrap_or(0) }),
)
.await?;
return Ok(Json(json!({ "source": "station", "response": response })));
}
let station = load(&app, &identity).await?;
Ok(Json(
json!({ "cost_mode": station.cost_mode, "default_tariffs": db::tariffs::defaults(&app.db, station.id).await? }),
))
}
#[derive(Debug, Deserialize)]
pub struct AssignDefault {
#[serde(default)]
pub evse_id: i64,
pub tariff_id: String,
}
pub async fn station_set(
State(app): State<App>,
Path(identity): Path<String>,
Json(body): Json<AssignDefault>,
) -> Result<Json<Value>, AppError> {
let station = load(&app, &identity).await?;
Ok(Json(
tariff::set_default(&app, &station, body.evse_id, &body.tariff_id).await?,
))
}
#[derive(Debug, Deserialize)]
pub struct ClearQuery {
pub evse_id: Option<i64>,
}
pub async fn station_clear(
State(app): State<App>,
Path((identity, tariff_id)): Path<(String, String)>,
Query(q): Query<ClearQuery>,
) -> Result<Json<Value>, AppError> {
let station = load(&app, &identity).await?;
Ok(Json(
tariff::clear_default(&app, &station, &tariff_id, q.evse_id).await?,
))
}
#[derive(Debug, Deserialize)]
pub struct ChangeTariff {
pub tariff_id: String,
}
pub async fn change_transaction(
State(app): State<App>,
Path(id): Path<i64>,
Json(body): Json<ChangeTariff>,
) -> Result<Json<Value>, AppError> {
Ok(Json(
json!({ "response": tariff::change_transaction_tariff(&app, id, &body.tariff_id).await? }),
))
}
pub async fn transaction_cost(
State(app): State<App>,
Path(id): Path<i64>,
) -> Result<Json<Value>, AppError> {
Ok(Json(json!(tariff::report(&app, id).await?)))
}