use icann_rdap_common::rdns::ip_to_reverse_dns;
use std::net::IpAddr;
use std::str::FromStr;
use {
    icann_rdap_client::{
        http::{ClientConfig, create_client},
        rdap::{QueryType, rdap_request},
    },
    icann_rdap_common::prelude::*,
    icann_rdap_srv::storage::{
        StoreOps,
        data::{NetworkId, NetworkIdType},
    },
};

use crate::test_jig::SrvTestJig;

#[tokio::test]
async fn ipv4_reverse_domain_200() {
    // GIVEN
    let test_srv = SrvTestJig::new().await;
    let mut tx = test_srv.mem.new_tx().await.expect("new transaction");

    // Create a domain response for reverse DNS with associated network
    let domain = Domain::builder()
        .ldh_name("4.3.2.1.in-addr.arpa")
        .network(
            Network::builder()
                .cidr("1.2.3.4/32")
                .build()
                .expect("cidr parsing"),
        )
        .build();

    tx.add_domain(&domain).await.expect("adding domain");
    tx.commit().await.expect("tx commit");

    // WHEN
    let client_config = ClientConfig::builder()
        .https_only(false)
        .follow_redirects(true)
        .build();
    let client = create_client(&client_config).expect("creating client");
    let query = QueryType::rdns_ipv4("4.3.2.1.in-addr.arpa").expect("invalid reverse DNS");
    let response = rdap_request(&test_srv.rdap_base, &query, &client)
        .await
        .expect("querying server");

    // THEN
    assert_eq!(response.http_data.status_code, 200);
    assert!(matches!(response.rdap, RdapResponse::Domain(_)));
}

#[tokio::test]
async fn ipv6_reverse_domain_200() {
    // GIVEN
    let test_srv = SrvTestJig::new().await;
    let mut tx = test_srv.mem.new_tx().await.expect("new transaction");

    // Create a domain response for IPv6 reverse DNS
    // Store the domain that will be generated by ip_to_reverse_dns
    let ldh_name = ip_to_reverse_dns(&IpAddr::from_str("2001:db8::567:89ab").expect("valid ipv6"));
    let domain = Domain::builder()
        .ldh_name(ldh_name)
        .network(
            Network::builder()
                .cidr("2001:db8::567:89ab/128")
                .build()
                .expect("cidr parsing"),
        )
        .build();

    tx.add_domain(&domain).await.expect("adding domain");
    tx.commit().await.expect("tx commit");

    // WHEN
    let client_config = ClientConfig::builder()
        .https_only(false)
        .follow_redirects(true)
        .build();
    let client = create_client(&client_config).expect("creating client");
    let query = QueryType::rdns_ipstr("2001:db8::567:89ab").expect("invalid reverse DNS");
    let response = rdap_request(&test_srv.rdap_base, &query, &client)
        .await
        .expect("querying server");

    // THEN
    assert_eq!(response.http_data.status_code, 200);
    assert!(matches!(response.rdap, RdapResponse::Domain(_)));
}

// Bootstrap Redirect Tests

#[tokio::test]
async fn ipv4_bootstrap_redirect() {
    // GIVEN
    let test_srv = SrvTestJig::new_bootstrap().await;
    let mut tx = test_srv.mem.new_tx().await.expect("new transaction");

    // Add a network redirect that should apply to reverse DNS queries
    tx.add_network_err(
        &NetworkId::builder()
            .network_id(NetworkIdType::Cidr(ipnet::IpNet::V4(
                "192.0.2.0/24".parse().expect("parsing ipnet"),
            )))
            .build(),
        &icann_rdap_common::response::Rfc9083Error::redirect()
            .url("https://example.net/")
            .build(),
    )
    .await
    .expect("adding network redirect");

    tx.commit().await.expect("tx commit");

    // WHEN
    let client_config = ClientConfig::builder()
        .https_only(false)
        .follow_redirects(false)
        .build();
    let client = create_client(&client_config).expect("creating client");
    let query = QueryType::rdns_ipstr("192.0.2.100").expect("invalid IP");
    let response = rdap_request(&test_srv.rdap_base, &query, &client)
        .await
        .expect("querying server");

    // THEN
    assert!(response.rdap.is_redirect());
    assert_eq!(
        response
            .http_data
            .location
            .as_ref()
            .expect("no location header information"),
        "https://example.net/domain/100.2.0.192.in-addr.arpa"
    );
}

#[tokio::test]
async fn ipv6_bootstrap_redirect() {
    // GIVEN
    let test_srv = SrvTestJig::new_bootstrap().await;
    let mut tx = test_srv.mem.new_tx().await.expect("new transaction");

    // Add a network redirect that should apply to reverse DNS queries
    tx.add_network_err(
        &NetworkId::builder()
            .network_id(NetworkIdType::Cidr(ipnet::IpNet::V6(
                "2001:db8::567:89ab/128".parse().expect("parsing ipnet"),
            )))
            .build(),
        &icann_rdap_common::response::Rfc9083Error::redirect()
            .url("https://example.net/")
            .build(),
    )
    .await
    .expect("adding network redirect");

    tx.commit().await.expect("tx commit");

    // WHEN
    let client_config = ClientConfig::builder()
        .https_only(false)
        .follow_redirects(false)
        .build();
    let client = create_client(&client_config).expect("creating client");
    let query = QueryType::rdns_ipstr("2001:db8::567:89ab").expect("invalid IP");
    let response = rdap_request(&test_srv.rdap_base, &query, &client)
        .await
        .expect("querying server");

    // THEN
    let rdns = ip_to_reverse_dns(&IpAddr::from_str("2001:db8::567:89ab").expect("valid ipv6"));
    assert!(response.rdap.is_redirect());
    assert_eq!(
        response
            .http_data
            .location
            .as_ref()
            .expect("no location header information"),
        &format!("https://example.net/domain/{rdns}")
    );
}
