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

use anyhow::Result;

use httpmock::MockServer;
use pdk_test::port::Port;
#[cfg(feature = "experimental")]
use pdk_test::services::flex::UpstreamServiceConfig;
use pdk_test::services::flex::{ApiConfig, Flex, FlexConfig, PolicyConfig};

use pdk_test::services::httpmock::{HttpMock, HttpMockConfig};
use pdk_test::{pdk_test, TestComposite};
use reqwest::StatusCode;

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

#[pdk_test]
async fn declarative_config() -> Result<()> {
    // Configure the upstream service
    let upstream_config = HttpMockConfig::builder().hostname("mock").port(80).build();

    let policy_config = PolicyConfig::builder()
        .name("header-injection-flex")
        .configuration(serde_json::json!({"outboundHeaders": [{
            "key": "custom",
            "value": "value"
        }]}))
        .build();

    let api_config = ApiConfig::builder()
        .name("myApi")
        .upstream(&upstream_config)
        .path("/anything/echo/")
        .port(FLEX_PORT)
        .policies([policy_config])
        .build();

    // -- SETUP logic
    // Configure Flex Gateway
    let flex_config = FlexConfig::builder()
        .version("1.10.0")
        .with_api(api_config)
        .config_mounts([(COMMONS_DIR, "common")])
        .build();

    // Compose the services
    let composite = TestComposite::builder()
        .with_service(flex_config)
        .with_service(upstream_config)
        .build()
        .await?;

    // Get a handle to the Flex service
    let flex: Flex = composite.service()?;

    let http_mock: HttpMock = composite.service()?;

    // Get a handle to the upstream service
    let upstream = MockServer::connect_async(http_mock.socket()).await;

    // Get an external URL to point the Flex service
    let flex_url = flex.external_url(FLEX_PORT).unwrap();

    // -- TEST logic

    // Mock upstream service interactions
    upstream
        .mock_async(|when, then| {
            when.path_contains("/hello");
            then.status(202).body("World!");
        })
        .await;

    let response = reqwest::get(format!("{flex_url}/hello")).await?;

    let status = response.status();
    let header = response
        .headers()
        .get("custom")
        .unwrap()
        .to_str()
        .unwrap()
        .to_string();
    let body = response.text().await.unwrap();

    assert_eq!(status, StatusCode::ACCEPTED);
    assert_eq!(body, "World!");
    assert_eq!(header.as_str(), "value");

    Ok(())
}

#[pdk_test]
#[cfg(feature = "experimental")]
async fn declarative_config_with_service() -> Result<()> {
    // Configure the upstream service
    let upstream_config = HttpMockConfig::builder().hostname("mock").port(80).build();

    let policy_config = PolicyConfig::builder()
        .name("health-check")
        .configuration(serde_json::json!({"endpoint": "http://healthcheck", "path": "/status", "statusCode": "200", "interval": 1}))
        .build();

    let api_config = ApiConfig::builder()
        .name("myApi")
        .upstream(&upstream_config)
        .path("/anything/echo/")
        .port(FLEX_PORT)
        .policies([policy_config])
        .build();

    let service_config = UpstreamServiceConfig::builder()
        .name("healthcheck")
        .address("http://mock")
        .build();

    // -- SETUP logic
    // Configure Flex Gateway
    let flex_config = FlexConfig::builder()
        .version("1.10.0")
        .with_api(api_config)
        .with_upstream_service(service_config)
        .config_mounts([(COMMONS_DIR, "common")])
        .build();

    // Compose the services
    let composite = TestComposite::builder()
        .with_service(flex_config)
        .with_service(upstream_config)
        .build()
        .await?;

    // Get a handle to the Flex service
    let flex: Flex = composite.service()?;

    let http_mock: HttpMock = composite.service()?;

    // Get a handle to the upstream service
    let upstream = MockServer::connect_async(http_mock.socket()).await;

    // Get an external URL to point the Flex service
    let flex_url = flex.external_url(FLEX_PORT).unwrap();

    // -- TEST logic

    // Mock upstream service interactions
    upstream
        .mock_async(|when, then| {
            when.path_contains("/hello");
            then.status(202).body("World!");
        })
        .await;
    upstream
        .mock_async(|when, then| {
            when.path("/status");
            then.status(200);
        })
        .await;

    let response = reqwest::get(format!("{flex_url}/hello")).await?;

    let status = response.status();
    let body = response.text().await.unwrap();

    assert_eq!(status, StatusCode::ACCEPTED);
    assert_eq!(body, "World!");

    Ok(())
}
