Documentation
// Copyright (c) 2025, Salesforce, Inc.,
// All rights reserved.
// For full license text, see the LICENSE.txt file

use std::time::Duration;

use anyhow::Result;

use httpmock::MockServer;
use pdk_test::port::Port;
use pdk_test::services::flex::{Flex, FlexConfig};
use pdk_test::services::httpbin::HttpBinConfig;
use pdk_test::services::httpmock::{HttpMock, HttpMockConfig};
use pdk_test::{pdk_test, TestComposite, TestError};

const COMMONS_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/commons");
const CONFIG_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/configs");
const FLEX_PORT: Port = 8081;

#[pdk_test]
async fn httpmock_backend() -> Result<()> {
    let flex_config = FlexConfig::builder()
        .config_mounts([(CONFIG_DIR, "configs"), (COMMONS_DIR, "commons")])
        .ports([FLEX_PORT])
        .timeout(Duration::from_secs(60))
        .build();

    let httpmock_config = HttpMockConfig::builder().port(80).build();

    let composite = TestComposite::builder()
        .with_service(flex_config)
        .with_service(httpmock_config)
        .build()
        .await?;

    let flex: Flex = composite.service()?;
    let httpmock: HttpMock = composite.service()?;

    let mock_server = MockServer::connect_async(httpmock.socket()).await;

    mock_server
        .mock_async(|when, then| {
            when.path_contains("/hello");
            then.status(200).body("World!");
        })
        .await;

    let client = reqwest::Client::new();

    let response = client
        .get(format!("{}/hello", flex.external_url(FLEX_PORT).unwrap()))
        .send()
        .await?;

    assert_eq!(response.status(), 200);
    assert_eq!(
        response.headers()["server"].to_str()?,
        "Anypoint Flex Gateway"
    );
    assert_eq!(response.text().await?, "World!");

    Ok(())
}

#[pdk_test]
async fn invalid_explicit_repository() {
    let flex_config = FlexConfig::builder()
        .ports([FLEX_PORT])
        .image_name("unknown/flex-gateway")
        .build();

    let result = TestComposite::builder()
        .with_service(flex_config)
        .build()
        .await;

    assert!(result.is_err());

    let Err(error) = result else {
        unreachable!();
    };

    assert!(matches!(error, TestError::Docker(_)));
}

#[pdk_test]
async fn valid_explicit_repository() {
    let flex_config = FlexConfig::builder().ports([FLEX_PORT]).build();

    let result = TestComposite::builder()
        .with_service(flex_config)
        .build()
        .await;

    assert!(result.is_ok());
}

#[test]
fn upload_configs() -> Result<()> {
    ::pdk_test::__runner::Test::builder()
        .module("flex")
        .name("force_upload")
        .force_upload()
        .build()
        .run(async {
            let flex_config = FlexConfig::builder()
                .config_mounts([(CONFIG_DIR, "configs"), (COMMONS_DIR, "commons")])
                .ports([FLEX_PORT])
                .timeout(Duration::from_secs(60))
                .build();

            let composite = TestComposite::builder()
                .with_service(flex_config)
                .with_service(HttpBinConfig::default())
                .build()
                .await?;

            let flex: Flex = composite.service()?;

            let client = reqwest::Client::new();

            let response = client
                .get(format!("{}/get", flex.external_url(FLEX_PORT).unwrap()))
                .send()
                .await?;

            assert_eq!(response.status(), 200);
            assert_eq!(
                response.headers()["server"].to_str()?,
                "Anypoint Flex Gateway"
            );

            Ok(())
        })
}

#[ignore = "W-14711920"]
#[pdk_test]
async fn httpbin_backend() -> Result<()> {
    let flex_config = FlexConfig::builder()
        .config_mounts([(CONFIG_DIR, "configs")])
        .ports([FLEX_PORT])
        .timeout(Duration::from_secs(60))
        .build();

    let composite = TestComposite::builder()
        .with_service(flex_config)
        .with_service(HttpBinConfig::default())
        .build()
        .await?;

    let flex: Flex = composite.service()?;

    let client = reqwest::Client::new();

    let response = client
        .get(format!("{}/get", flex.external_url(FLEX_PORT).unwrap()))
        .send()
        .await?;

    assert_eq!(response.status(), 200);
    assert_eq!(
        response.headers()["server"].to_str()?,
        "Anypoint Flex Gateway"
    );

    Ok(())
}