use crate::client::HafasClient;
use crate::parse::locations_response::HafasLocationsResponse;
use crate::{Client, Place, Result};
#[cfg(feature = "wasm-bindings")]
use js_sys::Promise;
use serde::Deserialize;
use serde::Serialize;
use serde_json::json;
#[cfg(feature = "wasm-bindings")]
use wasm_bindgen::prelude::wasm_bindgen;
#[cfg(feature = "wasm-bindings")]
use wasm_bindgen::JsValue;
#[cfg(feature = "wasm-bindings")]
use wasm_bindgen_futures::future_to_promise;

pub type LocationsResponse = Vec<Place>;

#[derive(Debug, Default, Deserialize, Serialize)]
pub struct LocationsOptions {
    pub query: String,
    pub results: Option<u64>,
    pub language: Option<String>,
}

impl HafasClient {
    pub async fn locations(&self, opts: LocationsOptions) -> Result<LocationsResponse> {
        let data: HafasLocationsResponse = self
            .request(json!({
                "svcReqL": [
                    {
                        "cfg": {
                            "polyEnc": "GPA"
                        },
                        "meth": "LocMatch",
                        "req": {
                            "input": {
                                "loc": {
                                    "type": "ALL",
                                    "name": format!("{}?", opts.query),
                                },
                                "maxLoc": opts.results.unwrap_or(10),
                                "field": "S"
                            }
                        }
                    }
                ],
                "lang": opts.language.as_deref().unwrap_or_else(|| self.profile.language()),
            }))
            .await?;

        Ok(self.profile.parse_locations_response(data)?)
    }
}

#[cfg(feature = "wasm-bindings")]
#[wasm_bindgen]
impl HafasClient {
    #[wasm_bindgen(js_name = "locations")]
    pub fn wasm_locations(&self, opts: JsValue) -> Promise {
        let client = self.clone();
        future_to_promise(async move {
            let opts = opts.into_serde().map_err(|e| e.to_string())?;
            let res = client.locations(opts).await.map_err(|e| e.to_string())?;
            Ok(JsValue::from_serde(&res).map_err(|e| e.to_string())?)
        })
    }
}
