use axum::response::IntoResponse;
use http::HeaderMap;
use validator::Validate;

use crate::fixtures::petstore_server::*;

#[test]
fn test_list_pets_request_compiles() {
  let request = ListPetsRequest::builder()
    .api_version("v1".to_string())
    .x_sort_order(ListPetsRequestHeaderXSortOrder::Asc)
    .x_only(vec![ListPetsRequestHeaderXonly::Bird, ListPetsRequestHeaderXonly::Fish])
    .limit(50)
    .build();
  assert!(request.is_ok(), "request should be valid");
  let request = request.unwrap();
  assert_eq!(request.query.limit, Some(50), "limit should be 50");

  let headers: HeaderMap = request.header.try_into().unwrap();
  assert_eq!(
    headers.get("x-sort-order").unwrap().to_str().unwrap(),
    "asc",
    "header map should contain x-sort-order"
  );
  assert_eq!(
    headers.get("x-only").unwrap().to_str().unwrap(),
    "bird,fish",
    "header map should contain x-only values"
  );
}

#[test]
fn test_list_pets_request_query_validation() {
  let valid_query = ListPetsRequestQuery { limit: Some(50) };
  assert!(valid_query.validate().is_ok(), "limit=50 should be valid");

  let min_query = ListPetsRequestQuery { limit: Some(1) };
  assert!(min_query.validate().is_ok(), "limit=1 should be valid");

  let max_query = ListPetsRequestQuery { limit: Some(100) };
  assert!(max_query.validate().is_ok(), "limit=100 should be valid");

  let none_query = ListPetsRequestQuery { limit: None };
  assert!(none_query.validate().is_ok(), "limit=None should be valid");

  let below_min_query = ListPetsRequestQuery { limit: Some(0) };
  assert!(
    below_min_query.validate().is_err(),
    "limit=0 should fail validation (below min=1)"
  );

  let above_max_query = ListPetsRequestQuery { limit: Some(101) };
  assert!(
    above_max_query.validate().is_err(),
    "limit=101 should fail validation (above max=100)"
  );
}

#[test]
fn test_show_pet_by_id_request_compiles() {
  let request = ShowPetByIdRequest::builder()
    .pet_id("pet-123".to_string())
    .x_api_version("v1".to_string())
    .build();
  assert!(request.is_ok(), "request should be valid");
  let request = request.unwrap();
  assert_eq!(request.path.pet_id, "pet-123", "pet_id should match");
  assert_eq!(request.header.x_api_version, "v1", "x_api_version should match");
}

#[test]
fn test_show_pet_by_id_path_validation() {
  let valid_path = ShowPetByIdRequestPath {
    pet_id: "1".to_string(),
  };
  assert!(valid_path.validate().is_ok(), "non-empty pet_id should be valid");

  let empty_path = ShowPetByIdRequestPath { pet_id: String::new() };
  assert!(
    empty_path.validate().is_err(),
    "empty pet_id should fail validation (min length=1)"
  );
}

#[test]
fn test_show_pet_by_id_header_validation() {
  let valid_header = ShowPetByIdRequestHeader {
    x_api_version: "v1".to_string(),
  };
  assert!(
    valid_header.validate().is_ok(),
    "non-empty x_api_version should be valid"
  );

  let empty_header = ShowPetByIdRequestHeader {
    x_api_version: String::new(),
  };
  assert!(
    empty_header.validate().is_err(),
    "empty x_api_version should fail validation (min length=1)"
  );
}

#[test]
fn test_show_pet_by_id_header_to_header_map() {
  let header = ShowPetByIdRequestHeader {
    x_api_version: "v2".to_string(),
  };
  let header_map: http::HeaderMap = header.try_into().expect("valid header");
  assert_eq!(
    header_map.get("x-api-version").map(|v| v.to_str().unwrap()),
    Some("v2"),
    "header map should contain x-api-version"
  );
}

#[test]
fn test_create_pets_request_compiles() {
  let request = CreatePetsRequest::builder()
    .api_version("v1".to_string())
    .build()
    .unwrap();
  assert!(request.validate().is_ok(), "request with api_version should be valid");
  assert_eq!(request.path.api_version, "v1", "api_version should match");
}

#[test]
fn test_pet_struct_compiles() {
  let pet = Pet {
    id: 1,
    name: "Fluffy".to_string(),
    tag: Some("cat".to_string()),
    ..Default::default()
  };
  assert_eq!(pet.id, 1, "id should match");
  assert_eq!(pet.name, "Fluffy", "name should match");
  assert_eq!(pet.tag, Some("cat".to_string()), "tag should match");
}

#[test]
fn test_error_struct_compiles() {
  let error = Error {
    code: 404,
    message: "Not found".to_string(),
  };
  assert_eq!(error.code, 404, "code should match");
  assert_eq!(error.message, "Not found", "message should match");
}

#[test]
fn test_pets_type_alias() {
  let pets: Pets = vec![
    Pet {
      id: 1,
      name: "Fluffy".to_string(),
      tag: None,
      ..Default::default()
    },
    Pet {
      id: 2,
      name: "Rex".to_string(),
      tag: Some("dog".to_string()),
      ..Default::default()
    },
  ];
  assert_eq!(pets.len(), 2, "should have 2 pets");
}

#[test]
fn test_query_deserialization() {
  let query: ListPetsRequestQuery = serde_json::from_str(r#"{"limit": 10}"#).expect("deserialization should succeed");
  assert_eq!(query.limit, Some(10), "limit should be deserialized");

  let query_none: ListPetsRequestQuery = serde_json::from_str(r"{}").expect("deserialization should succeed");
  assert_eq!(query_none.limit, None, "missing fields should be None");
}

#[test]
fn test_list_pets_response_into_response() {
  let ok_response = ListPetsResponse::Ok(vec![Pet {
    id: 1,
    name: "Fluffy".to_string(),
    tag: None,
    ..Default::default()
  }]);
  let response = ok_response.into_response();
  assert_eq!(
    response.status(),
    http::StatusCode::OK,
    "ok response should have 200 status"
  );

  let error_response = ListPetsResponse::Unknown(Error {
    code: 500,
    message: "Internal error".to_string(),
  });
  let response = error_response.into_response();
  assert_eq!(
    response.status(),
    http::StatusCode::OK,
    "unknown response uses default status"
  );
}

#[test]
fn test_show_pet_by_id_response_into_response() {
  let ok_response = ShowPetByIdResponse::Ok(Pet {
    id: 42,
    name: "Rex".to_string(),
    tag: Some("dog".to_string()),
    ..Default::default()
  });
  let response = ok_response.into_response();
  assert_eq!(
    response.status(),
    http::StatusCode::OK,
    "ok response should have 200 status"
  );
}

#[test]
fn test_create_pets_response_into_response() {
  let created_response = CreatePetsResponse::Created;
  let response = created_response.into_response();
  assert_eq!(
    response.status(),
    http::StatusCode::CREATED,
    "created response should have 201 status"
  );
}
