//! JSON wrapper around `car-integrations` for CLI/bindings/server.
//!
//! Error codes: `unavailable`, `backend`, `not_implemented`, `parse`.

use chrono::{DateTime, Utc};
use serde_json::{json, Value};

fn err_json(e: car_integrations::IntegrationError) -> String {
    use car_integrations::IntegrationError::*;
    let code = match &e {
        Unavailable(_) => "unavailable",
        Backend(_) => "backend",
        NotImplemented(_) => "not_implemented",
    };
    json!({"code": code, "message": e.to_string()}).to_string()
}

fn val_err(e: impl std::fmt::Display) -> String {
    json!({"code": "backend", "message": e.to_string()}).to_string()
}

fn parse_err(msg: String) -> String {
    json!({"code": "parse", "message": msg}).to_string()
}

// ---------- Calendar ----------

pub fn calendar_list() -> Result<Value, String> {
    let r = car_integrations::calendar::list_calendars().map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn calendar_events(
    start: DateTime<Utc>,
    end: DateTime<Utc>,
    calendar_ids: &[String],
) -> Result<Value, String> {
    let r = car_integrations::calendar::list_events(start, end, calendar_ids).map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn calendar_create_event(input_json: &str) -> Result<Value, String> {
    let input: car_integrations::calendar::EventCreateInput = serde_json::from_str(input_json)
        .map_err(|e| parse_err(format!("parse EventCreateInput: {}", e)))?;
    let r = car_integrations::calendar::create_event(input).map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn calendar_update_event(input_json: &str) -> Result<Value, String> {
    let input: car_integrations::calendar::EventUpdateInput = serde_json::from_str(input_json)
        .map_err(|e| parse_err(format!("parse EventUpdateInput: {}", e)))?;
    let r = car_integrations::calendar::update_event(input).map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn calendar_delete_event(event_id: &str) -> Result<Value, String> {
    let r = car_integrations::calendar::delete_event(event_id).map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

// ---------- Contacts ----------

pub fn contacts_containers() -> Result<Value, String> {
    let r = car_integrations::contacts::list_containers().map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn contacts_list(query: &str, container_ids: &[String], limit: usize) -> Result<Value, String> {
    let r =
        car_integrations::contacts::list_contacts(query, container_ids, limit).map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

// ---------- Mail ----------

pub fn mail_accounts() -> Result<Value, String> {
    let r = car_integrations::mail::list_accounts().map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn mail_inbox(account_ids: &[String]) -> Result<Value, String> {
    let r = car_integrations::mail::list_inbox(account_ids).map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn mail_send(req_json: &str) -> Result<Value, String> {
    let req: car_integrations::mail::SendRequest = serde_json::from_str(req_json)
        .map_err(|e| parse_err(format!("parse SendRequest: {}", e)))?;
    let r = car_integrations::mail::send(req).map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

// ---------- Messages ----------

pub fn messages_services() -> Result<Value, String> {
    let r = car_integrations::messages::list_services().map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn messages_chats(limit: usize) -> Result<Value, String> {
    let r = car_integrations::messages::list_chats(limit).map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn messages_send(req_json: &str) -> Result<Value, String> {
    let req: car_integrations::messages::SendRequest = serde_json::from_str(req_json)
        .map_err(|e| parse_err(format!("parse Messages SendRequest: {}", e)))?;
    let r = car_integrations::messages::send(req).map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

// ---------- Apple account-backed surfaces ----------

pub fn notes_accounts() -> Result<Value, String> {
    let r = car_integrations::apple::notes_accounts().map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn notes_find(query: &str, limit: usize) -> Result<Value, String> {
    let r = car_integrations::apple::notes_find(query, limit).map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn reminders_lists() -> Result<Value, String> {
    let r = car_integrations::apple::reminders_lists().map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn reminders_items(limit: usize) -> Result<Value, String> {
    let r = car_integrations::apple::reminders_items(limit).map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn photos_albums() -> Result<Value, String> {
    let r = car_integrations::apple::photos_albums().map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn bookmarks_list(limit: usize) -> Result<Value, String> {
    let r = car_integrations::apple::bookmarks_list(limit).map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn files_locations() -> Result<Value, String> {
    let r = car_integrations::apple::files_locations().map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}

pub fn keychain_status() -> Result<Value, String> {
    let r = car_integrations::apple::keychain_status().map_err(err_json)?;
    serde_json::to_value(&r).map_err(val_err)
}
