use crate::common::data::{
    ForwardingRuleConfig, MockServerHttpResponse, ProxyRuleConfig, RecordingRuleConfig,
};
use std::{borrow::Borrow, net::SocketAddr, sync::Arc};

use crate::api::{
    adapter::{
        ServerAdapterError,
        ServerAdapterError::{
            InvalidMockDefinitionError, JsonDeserializationError, JsonSerializationError,
            UpstreamError,
        },
    },
    MockServerAdapter,
};
use async_trait::async_trait;
use bytes::Bytes;
use http::{Request, StatusCode};

use crate::common::{
    data::{
        ActiveForwardingRule, ActiveMock, ActiveProxyRule, ActiveRecording, ClosestMatch,
        MockDefinition, RequestRequirements,
    },
    http::HttpClient,
};

pub struct RemoteMockServerAdapter {
    addr: SocketAddr,
    http_client: Arc<dyn HttpClient + Send + Sync + 'static>,
}

impl RemoteMockServerAdapter {
    pub fn new(addr: SocketAddr, http_client: Arc<dyn HttpClient + Send + Sync + 'static>) -> Self {
        Self { addr, http_client }
    }

    fn validate_request_requirements(
        &self,
        requirements: &RequestRequirements,
    ) -> Result<(), ServerAdapterError> {
        match requirements.is_true {
            Some(_) => Err(InvalidMockDefinitionError(
                "Anonymous function request matchers are not supported when using a remote mock server".to_string(),
            )),
            None => Ok(()),
        }
    }

    fn validate_response(
        &self,
        response: &MockServerHttpResponse,
    ) -> Result<(), ServerAdapterError> {
        match response.respond_with {
            Some(_) => Err(InvalidMockDefinitionError(
                "Dynamic responders are not supported by remote/standalone servers".to_string(),
            )),
            None => Ok(()),
        }
    }

    async fn do_request(&self, req: Request<Bytes>) -> Result<(u16, String), ServerAdapterError> {
        let (code, body_bytes) = self.do_request_raw(req).await?;

        let body =
            String::from_utf8(body_bytes.to_vec()).map_err(|e| UpstreamError(e.to_string()))?;

        Ok((code, body))
    }

    async fn do_request_raw(
        &self,
        req: Request<Bytes>,
    ) -> Result<(u16, Bytes), ServerAdapterError> {
        let mut response = self
            .http_client
            .send(req)
            .await
            .map_err(|e| UpstreamError(e.to_string()))?;

        Ok((response.status().as_u16(), response.body().clone()))
    }
}

#[async_trait]
impl MockServerAdapter for RemoteMockServerAdapter {
    fn host(&self) -> String {
        self.addr.ip().to_string()
    }

    fn port(&self) -> u16 {
        self.addr.port()
    }

    fn address(&self) -> &SocketAddr {
        &self.addr
    }

    async fn reset(&self) -> Result<(), ServerAdapterError> {
        let request = Request::builder()
            .method("DELETE")
            .uri(format!("http://{}/__httpmock__/state", &self.addr))
            .body(Bytes::new())
            .map_err(|e| UpstreamError(e.to_string()))?;

        let (status, body) = self.do_request(request).await?;

        if status != StatusCode::NO_CONTENT {
            return Err(UpstreamError(format!(
                "Could not reset the mock server. Expected response status 204 but was {} (response body = '{}')",
                status, body
            )));
        }

        Ok(())
    }

    async fn create_mock(&self, mock: &MockDefinition) -> Result<ActiveMock, ServerAdapterError> {
        self.validate_request_requirements(&mock.request)?;
        self.validate_response(&mock.response)?;

        let json = serde_json::to_string(mock).map_err(|e| JsonSerializationError(e))?;

        let request = Request::builder()
            .method("POST")
            .uri(format!("http://{}/__httpmock__/mocks", &self.address()))
            .header("content-type", "application/json")
            .body(Bytes::from(json))
            .map_err(|e| UpstreamError(e.to_string()))?;

        let (status, body) = self.do_request(request).await?;

        if status != StatusCode::CREATED.as_u16() {
            return Err(UpstreamError(format!(
                "Could not create mock. Expected response status 201 but was {} (response body = '{}')",
                status, body
            )));
        }

        let response: ActiveMock =
            serde_json::from_str(&body).map_err(|e| JsonDeserializationError(e))?;

        Ok(response)
    }

    async fn fetch_mock(&self, mock_id: usize) -> Result<ActiveMock, ServerAdapterError> {
        let request = Request::builder()
            .method("GET")
            .uri(format!(
                "http://{}/__httpmock__/mocks/{}",
                &self.address(),
                mock_id
            ))
            .body(Bytes::new())
            .map_err(|e| UpstreamError(e.to_string()))?;

        let (status, body) = self.do_request(request).await?;

        if status != StatusCode::OK {
            return Err(UpstreamError(format!(
                "Could not fetch mock from the mock server. Expected response status 200 but was {} (response body = '{}')",
                status, body
            )));
        }

        let response: ActiveMock =
            serde_json::from_str(&body).map_err(|e| JsonDeserializationError(e))?;

        Ok(response)
    }

    async fn delete_mock(&self, mock_id: usize) -> Result<(), ServerAdapterError> {
        let request = Request::builder()
            .method("DELETE")
            .uri(format!(
                "http://{}/__httpmock__/mocks/{}",
                &self.address(),
                mock_id
            ))
            .body(Bytes::new())
            .map_err(|e| UpstreamError(e.to_string()))?;

        let (status, body) = self.do_request(request).await?;

        if status != StatusCode::NO_CONTENT {
            return Err(UpstreamError(format!(
                "Could not delete mock from the mock server. Expected response status 204 but was {} (response body = '{}')",
                status, body
            )));
        }

        Ok(())
    }

    async fn verify(
        &self,
        requirements: &RequestRequirements,
    ) -> Result<Option<ClosestMatch>, ServerAdapterError> {
        let json = serde_json::to_string(requirements).map_err(|e| JsonSerializationError(e))?;

        let request = Request::builder()
            .method("POST")
            .uri(format!("http://{}/__httpmock__/verify", &self.address()))
            .header("content-type", "application/json")
            .body(Bytes::from(json))
            .map_err(|e| UpstreamError(e.to_string()))?;

        let (status, body) = self.do_request(request).await?;

        if status == StatusCode::NOT_FOUND {
            return Ok(None);
        }

        if status != StatusCode::OK {
            return Err(UpstreamError(format!(
                "Could not verify mock. Expected response status 200 but was {} (response body = '{}')",
                status, body
            )));
        }

        let response: ClosestMatch =
            serde_json::from_str(&body).map_err(|e| JsonDeserializationError(e))?;

        Ok(Some(response))
    }

    async fn create_forwarding_rule(
        &self,
        config: ForwardingRuleConfig,
    ) -> Result<ActiveForwardingRule, ServerAdapterError> {
        self.validate_request_requirements(&config.request_requirements)?;

        let json = serde_json::to_string(&config).map_err(|e| JsonSerializationError(e))?;

        let request = Request::builder()
            .method("POST")
            .uri(format!(
                "http://{}/__httpmock__/forwarding_rules",
                &self.address()
            ))
            .header("content-type", "application/json")
            .body(Bytes::from(json))
            .map_err(|e| UpstreamError(e.to_string()))?;

        let (status, body) = self.do_request(request).await?;

        if status != StatusCode::CREATED.as_u16() {
            return Err(UpstreamError(format!(
                "Could not create forwarding rule. Expected response status 201 but was {} (response body = '{}')",
                status, body
            )));
        }

        let response: ActiveForwardingRule =
            serde_json::from_str(&body).map_err(|e| JsonDeserializationError(e))?;

        Ok(response)
    }

    async fn delete_forwarding_rule(&self, id: usize) -> Result<(), ServerAdapterError> {
        let request = Request::builder()
            .method("DELETE")
            .uri(format!(
                "http://{}/__httpmock__/forwarding_rules/{}",
                &self.address(),
                id
            ))
            .body(Bytes::new())
            .map_err(|e| UpstreamError(e.to_string()))?;

        let (status, body) = self.do_request(request).await?;

        if status != StatusCode::NO_CONTENT {
            return Err(UpstreamError(format!(
                "Could not delete forwarding rule from the mock server. Expected response status 204 but was {} (response body = '{}')",
                status, body
            )));
        }

        Ok(())
    }

    async fn create_proxy_rule(
        &self,
        config: ProxyRuleConfig,
    ) -> Result<ActiveProxyRule, ServerAdapterError> {
        self.validate_request_requirements(&config.request_requirements)?;

        let json = serde_json::to_string(&config).map_err(|e| JsonSerializationError(e))?;

        let request = Request::builder()
            .method("POST")
            .uri(format!(
                "http://{}/__httpmock__/proxy_rules",
                &self.address()
            ))
            .header("content-type", "application/json")
            .body(Bytes::from(json))
            .map_err(|e| UpstreamError(e.to_string()))?;

        let (status, body) = self.do_request(request).await?;

        if status != StatusCode::CREATED.as_u16() {
            return Err(UpstreamError(format!(
                "Could not create proxy rule. Expected response status 201 but was {} (response body = '{}')",
                status, body
            )));
        }

        let response: ActiveProxyRule =
            serde_json::from_str(&body).map_err(|e| JsonDeserializationError(e))?;

        Ok(response)
    }

    async fn delete_proxy_rule(&self, id: usize) -> Result<(), ServerAdapterError> {
        let request = Request::builder()
            .method("DELETE")
            .uri(format!(
                "http://{}/__httpmock__/proxy_rules/{}",
                &self.address(),
                id
            ))
            .body(Bytes::new())
            .map_err(|e| UpstreamError(e.to_string()))?;

        let (status, body) = self.do_request(request).await?;

        if status != StatusCode::NO_CONTENT {
            return Err(UpstreamError(format!(
                "Could not delete proxy rule from the mock server. Expected response status 204 but was {} (response body = '{}')",
                status, body
            )));
        }

        Ok(())
    }

    async fn create_recording(
        &self,
        config: RecordingRuleConfig,
    ) -> Result<ActiveRecording, ServerAdapterError> {
        self.validate_request_requirements(&config.request_requirements)?;

        let json = serde_json::to_string(&config).map_err(|e| JsonSerializationError(e))?;

        let request = Request::builder()
            .method("POST")
            .uri(format!(
                "http://{}/__httpmock__/recordings",
                &self.address()
            ))
            .header("content-type", "application/json")
            .body(Bytes::from(json))
            .map_err(|e| UpstreamError(e.to_string()))?;

        let (status, body) = self.do_request(request).await?;

        if status != StatusCode::CREATED.as_u16() {
            return Err(UpstreamError(format!(
                "Could not create recording. Expected response status 201 but was {} (response body = '{}')",
                status, body
            )));
        }

        let response: ActiveRecording =
            serde_json::from_str(&body).map_err(|e| JsonDeserializationError(e))?;

        Ok(response)
    }

    async fn delete_recording(&self, id: usize) -> Result<(), ServerAdapterError> {
        let request = Request::builder()
            .method("DELETE")
            .uri(format!(
                "http://{}/__httpmock__/recordings/{}",
                &self.address(),
                id
            ))
            .body(Bytes::new())
            .map_err(|e| UpstreamError(e.to_string()))?;

        let (status, body) = self.do_request(request).await?;

        if status != StatusCode::NO_CONTENT {
            return Err(UpstreamError(format!(
                "Could not delete recording from the mock server. Expected response status 204 but was {} (response body = '{}')",
                status, body
            )));
        }

        Ok(())
    }

    #[cfg(feature = "record")]
    async fn export_recording(&self, id: usize) -> Result<Option<Bytes>, ServerAdapterError> {
        let request = Request::builder()
            .method("GET")
            .uri(format!(
                "http://{}/__httpmock__/recordings/{}",
                &self.address(),
                id
            ))
            .body(Bytes::new())
            .map_err(|e| UpstreamError(e.to_string()))?;

        let (status, body) = self.do_request_raw(request).await?;

        if status == StatusCode::NOT_FOUND {
            return Ok(None);
        } else if status != StatusCode::OK {
            return Err(UpstreamError(format!(
                "Could not fetch mock from the mock server. Expected response status 200 but was {}",
                status
            )));
        }

        Ok(Some(body))
    }

    #[cfg(feature = "record")]
    async fn create_mocks_from_recording<'a>(
        &self,
        recording_file_content: &'a str,
    ) -> Result<Vec<usize>, ServerAdapterError> {
        let request = Request::builder()
            .method("POST")
            .uri(format!(
                "http://{}/__httpmock__/recordings",
                &self.address(),
            ))
            .body(Bytes::from(recording_file_content.to_owned()))
            .map_err(|e| UpstreamError(e.to_string()))?;

        let (status, body) = self.do_request(request).await?;

        if status != StatusCode::OK {
            return Err(UpstreamError(format!(
                "Could not create mocks from recording. Expected response status 200 but was {}",
                status
            )));
        }

        let response: Vec<usize> =
            serde_json::from_str(&body).map_err(|e| JsonDeserializationError(e))?;

        Ok(response)
    }
}
