use super::RailwayApiClient;
use anyhow::Result;
use async_trait::async_trait;
use reqwest::Client;
use serde_json::Value;
pub struct OverpassApiClient {
    url: Option<String>,
}
#[cfg(target_arch = "wasm32")]
unsafe impl Send for OverpassApiClient {}
impl OverpassApiClient {
    pub fn new() -> Self {
        OverpassApiClient { url: None }
    }
    async fn fetch_by_query(&self, query: &str) -> Result<Value> {
        let client = Client::new();
        let form_data = [("data", query)];
        let response: Value = client
            .post(
                self.url
                    .as_deref()
                    .unwrap_or("https://overpass-api.de/api/interpreter"),
            )
            .form(&form_data)
            .send()
            .await?
            .json()
            .await?;
        Ok(response)
    }
}
impl Default for OverpassApiClient {
    fn default() -> Self {
        Self::new()
    }
}
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
impl RailwayApiClient for OverpassApiClient {
    async fn connect(&mut self, url: &str) -> Result<()> {
        self.url = Some(url.to_string());
        let client = Client::new();
        client.get(url).send().await?;
        Ok(())
    }
    async fn fetch_by_area_name(&self, area_name: &str) -> Result<Value> {
        let query = format!(
            r#"[out:json];area[name="{}"]->.searchArea;(way(area.searchArea)["railway"="rail"];node(area.searchArea)["railway"="switch"];node(area.searchArea)["railway"="buffer_stop"];node(area.searchArea)["railway"="railway_crossing"];);out geom;"#,
            area_name
        );
        let response: Value = self.fetch_by_query(&query).await?;
        Ok(response)
    }
    async fn fetch_by_bbox(&self, bbox: &str) -> Result<Value> {
        let query = format!(
            r#"[out:json];(way({})["railway"="rail"];node({})["railway"="switch"];node({})["railway"="buffer_stop"];node({})["railway"="railway_crossing"];);out geom;"#,
            bbox, bbox, bbox, bbox
        );
        let response: Value = self.fetch_by_query(&query).await?;
        Ok(response)
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use crate::tests::test_json_vilbel;
    use mockito::{mock, server_url};
    #[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
    #[cfg_attr(target_arch = "wasm32", ignore)]
    async fn test_connect() {
        let mock = mock("GET", "/").with_status(200).create();
        let mut client = OverpassApiClient::new();
        let result = client.connect(&server_url()).await;
        mock.assert();
        assert!(result.is_ok());
    }
    #[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
    #[cfg_attr(target_arch = "wasm32", ignore)]
    async fn test_fetch_by_area_name() {
        let test_json = test_json_vilbel();
        let query = r#"[out:json];area[name="Bad Vilbel"]->.searchArea;(way(area.searchArea)["railway"="rail"];node(area.searchArea)["railway"="switch"];node(area.searchArea)["railway"="buffer_stop"];node(area.searchArea)["railway"="railway_crossing"];);out geom;"#;
        let mock = mock("POST", "/api/interpreter")
            .with_status(200)
            .with_header("content-type", "application/json")
            .with_body(&serde_json::to_string(&test_json).unwrap())
            .match_header("content-type", "application/x-www-form-urlencoded")
            .match_body(mockito::Matcher::UrlEncoded(
                "data".to_string(),
                query.to_string(),
            ))
            .create();
        let mut client = OverpassApiClient::new();
        client
            .connect(&format!("{}/api/interpreter", server_url()))
            .await
            .unwrap();
        let result = client.fetch_by_area_name("Bad Vilbel").await;
        mock.assert();
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), test_json);
    }
    #[cfg_attr(not(target_arch = "wasm32"), tokio::test)]
    #[cfg_attr(target_arch = "wasm32", ignore)]
    async fn test_fetch_by_bbox() {
        let test_json = test_json_vilbel();
        let bbox = "1,2,3,4";
        let query = format!(
            r#"[out:json];(way({})["railway"="rail"];node({})["railway"="switch"];node({})["railway"="buffer_stop"];node({})["railway"="railway_crossing"];);out geom;"#,
            bbox, bbox, bbox, bbox
        );
        let mock = mock("POST", "/api/interpreter")
            .with_status(200)
            .with_header("content-type", "application/json")
            .with_body(&serde_json::to_string(&test_json).unwrap())
            .match_header("content-type", "application/x-www-form-urlencoded")
            .match_body(mockito::Matcher::UrlEncoded(
                "data".to_string(),
                query.to_string(),
            ))
            .create();
        let mut client = OverpassApiClient::new();
        client
            .connect(&format!("{}/api/interpreter", server_url()))
            .await
            .unwrap();
        let result = client.fetch_by_bbox(bbox).await;
        mock.assert();
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), test_json);
    }
}