use lmrc_cloudflare::CloudflareClient;
use lmrc_cloudflare::dns::RecordType;
use wiremock::matchers::{header, method, path, query_param};
use wiremock::{Mock, MockServer, ResponseTemplate};

#[tokio::test]
async fn test_list_dns_records() {
    let mock_server = MockServer::start().await;

    let response_body = r#"{
        "success": true,
        "errors": [],
        "messages": [],
        "result": [
            {
                "id": "record1",
                "type": "A",
                "name": "example.com",
                "content": "192.0.2.1",
                "proxied": true,
                "ttl": 1,
                "zone_id": "zone123",
                "zone_name": "example.com",
                "created_on": "2024-01-01T00:00:00Z",
                "modified_on": "2024-01-01T00:00:00Z"
            }
        ]
    }"#;

    Mock::given(method("GET"))
        .and(path("/zones/zone123/dns_records"))
        .and(header("authorization", "Bearer test-token"))
        .respond_with(ResponseTemplate::new(200).set_body_string(response_body))
        .mount(&mock_server)
        .await;

    let client = CloudflareClient::builder()
        .api_token("test-token")
        .base_url(mock_server.uri())
        .build()
        .unwrap();

    let records = client.dns().list_records("zone123").send().await.unwrap();

    assert_eq!(records.len(), 1);
    assert_eq!(records[0].id, "record1");
    assert_eq!(records[0].record_type, "A");
    assert_eq!(records[0].name, "example.com");
    assert_eq!(records[0].content, "192.0.2.1");
    assert!(records[0].proxied);
}

#[tokio::test]
async fn test_list_dns_records_with_filters() {
    let mock_server = MockServer::start().await;

    let response_body = r#"{
        "success": true,
        "errors": [],
        "messages": [],
        "result": []
    }"#;

    Mock::given(method("GET"))
        .and(path("/zones/zone123/dns_records"))
        .and(query_param("type", "A"))
        .and(query_param("name", "example.com"))
        .respond_with(ResponseTemplate::new(200).set_body_string(response_body))
        .mount(&mock_server)
        .await;

    let client = CloudflareClient::builder()
        .api_token("test-token")
        .base_url(mock_server.uri())
        .build()
        .unwrap();

    let _records = client
        .dns()
        .list_records("zone123")
        .record_type(RecordType::A)
        .name("example.com")
        .send()
        .await
        .unwrap();
}

#[tokio::test]
async fn test_get_dns_record() {
    let mock_server = MockServer::start().await;

    let response_body = r#"{
        "success": true,
        "errors": [],
        "messages": [],
        "result": {
            "id": "record1",
            "type": "A",
            "name": "example.com",
            "content": "192.0.2.1",
            "proxied": true,
            "ttl": 1,
            "zone_id": "zone123",
            "zone_name": "example.com"
        }
    }"#;

    Mock::given(method("GET"))
        .and(path("/zones/zone123/dns_records/record1"))
        .respond_with(ResponseTemplate::new(200).set_body_string(response_body))
        .mount(&mock_server)
        .await;

    let client = CloudflareClient::builder()
        .api_token("test-token")
        .base_url(mock_server.uri())
        .build()
        .unwrap();

    let record = client.dns().get_record("zone123", "record1").await.unwrap();

    assert_eq!(record.id, "record1");
    assert_eq!(record.name, "example.com");
}

#[tokio::test]
async fn test_create_dns_record() {
    let mock_server = MockServer::start().await;

    let response_body = r#"{
        "success": true,
        "errors": [],
        "messages": [],
        "result": {
            "id": "new-record",
            "type": "A",
            "name": "api.example.com",
            "content": "192.0.2.1",
            "proxied": true,
            "ttl": 1,
            "zone_id": "zone123",
            "zone_name": "example.com"
        }
    }"#;

    Mock::given(method("POST"))
        .and(path("/zones/zone123/dns_records"))
        .respond_with(ResponseTemplate::new(200).set_body_string(response_body))
        .mount(&mock_server)
        .await;

    let client = CloudflareClient::builder()
        .api_token("test-token")
        .base_url(mock_server.uri())
        .build()
        .unwrap();

    let record = client
        .dns()
        .create_record("zone123")
        .name("api.example.com")
        .record_type(RecordType::A)
        .content("192.0.2.1")
        .proxied(true)
        .send()
        .await
        .unwrap();

    assert_eq!(record.id, "new-record");
    assert_eq!(record.name, "api.example.com");
}

#[tokio::test]
async fn test_update_dns_record() {
    let mock_server = MockServer::start().await;

    let response_body = r#"{
        "success": true,
        "errors": [],
        "messages": [],
        "result": {
            "id": "record1",
            "type": "A",
            "name": "api.example.com",
            "content": "192.0.2.2",
            "proxied": true,
            "ttl": 1,
            "zone_id": "zone123",
            "zone_name": "example.com"
        }
    }"#;

    Mock::given(method("PUT"))
        .and(path("/zones/zone123/dns_records/record1"))
        .respond_with(ResponseTemplate::new(200).set_body_string(response_body))
        .mount(&mock_server)
        .await;

    let client = CloudflareClient::builder()
        .api_token("test-token")
        .base_url(mock_server.uri())
        .build()
        .unwrap();

    let record = client
        .dns()
        .update_record("zone123", "record1")
        .content("192.0.2.2")
        .send()
        .await
        .unwrap();

    assert_eq!(record.content, "192.0.2.2");
}

#[tokio::test]
async fn test_delete_dns_record() {
    let mock_server = MockServer::start().await;

    let response_body = r#"{
        "success": true,
        "errors": [],
        "messages": [],
        "result": {
            "id": "record1"
        }
    }"#;

    Mock::given(method("DELETE"))
        .and(path("/zones/zone123/dns_records/record1"))
        .respond_with(ResponseTemplate::new(200).set_body_string(response_body))
        .mount(&mock_server)
        .await;

    let client = CloudflareClient::builder()
        .api_token("test-token")
        .base_url(mock_server.uri())
        .build()
        .unwrap();

    client
        .dns()
        .delete_record("zone123", "record1")
        .await
        .unwrap();
}

#[tokio::test]
async fn test_api_error_handling() {
    let mock_server = MockServer::start().await;

    let error_response = r#"{
        "success": false,
        "errors": [
            {
                "code": 1003,
                "message": "Invalid zone identifier"
            }
        ],
        "messages": [],
        "result": null
    }"#;

    Mock::given(method("GET"))
        .and(path("/zones/invalid/dns_records"))
        .respond_with(ResponseTemplate::new(400).set_body_string(error_response))
        .mount(&mock_server)
        .await;

    let client = CloudflareClient::builder()
        .api_token("test-token")
        .base_url(mock_server.uri())
        .build()
        .unwrap();

    let result = client.dns().list_records("invalid").send().await;

    assert!(result.is_err());
}

#[tokio::test]
async fn test_unauthorized_error() {
    let mock_server = MockServer::start().await;

    Mock::given(method("GET"))
        .and(path("/zones/zone123/dns_records"))
        .respond_with(ResponseTemplate::new(401).set_body_string("Unauthorized"))
        .mount(&mock_server)
        .await;

    let client = CloudflareClient::builder()
        .api_token("invalid-token")
        .base_url(mock_server.uri())
        .build()
        .unwrap();

    let result = client.dns().list_records("zone123").send().await;

    assert!(result.is_err());
    match result.unwrap_err() {
        lmrc_cloudflare::Error::Unauthorized(_) => {}
        e => panic!("Expected Unauthorized error, got: {:?}", e),
    }
}

#[tokio::test]
async fn test_rate_limit_error() {
    let mock_server = MockServer::start().await;

    Mock::given(method("GET"))
        .and(path("/zones/zone123/dns_records"))
        .respond_with(
            ResponseTemplate::new(429)
                .insert_header("retry-after", "60")
                .set_body_string("Rate limit exceeded"),
        )
        .expect(4) // Initial + 3 retries
        .mount(&mock_server)
        .await;

    let client = CloudflareClient::builder()
        .api_token("test-token")
        .base_url(mock_server.uri())
        .build()
        .unwrap();

    let result = client.dns().list_records("zone123").send().await;

    assert!(result.is_err());
    match result.unwrap_err() {
        lmrc_cloudflare::Error::RateLimited { retry_after } => {
            assert_eq!(retry_after, Some(60));
        }
        e => panic!("Expected RateLimited error, got: {:?}", e),
    }
}
