use super::{
  CharacterMarketOrder,
  CorporationMarketOrder,
  MarketGroup,
  MarketHistory,
  MarketOrder,
  MarketOrderType,
  MarketPrice,
};
use crate::{
  get_authenticated,
  get_authenticated_paged,
  get_public,
  get_public_paged,
  Esi,
  EsiResult,
  Paged,
  Response,
};
use std::collections::HashMap;

impl Esi {
  /**
   * Get a list of orders in a character's orders
   * esi: https://esi.evetech.net/latest/characters/{character_id}/orders/
   */
  pub async fn get_character_orders(
    &self,
    access_token: &str,
    character_id: i32,
    etag: Option<&str>,
  ) -> EsiResult<Response<Vec<CharacterMarketOrder>>> {
    let result = get_authenticated::<Vec<CharacterMarketOrder>>(
      access_token,
      &format!("characters/{}/orders", character_id),
      self,
      None,
      etag,
    )
    .await?;
    Ok(result)
  }

  /**
   * Get a list of historical orders in a character's orders
   * esi: https://esi.evetech.net/latest/characters/{character_id}/orders/history/
   */
  pub async fn get_character_orders_history(
    &self,
    access_token: &str,
    character_id: i32,
    page: Option<i32>,
    etag: Option<&str>,
  ) -> EsiResult<Response<Paged<Vec<CharacterMarketOrder>>>> {
    let result = get_authenticated_paged::<Vec<CharacterMarketOrder>>(
      access_token,
      &format!("characters/{}/orders/history", character_id),
      self,
      Some(
        vec![("page", page.unwrap_or(1).to_string())]
          .into_iter()
          .collect(),
      ),
      etag,
    )
    .await?;
    Ok(result)
  }

  /**
   * Get a list of orders in a corporation's orders
   * esi: https://esi.evetech.net/latest/corporations/{corporation_id}/orders/
   */
  pub async fn get_corporation_orders(
    &self,
    access_token: &str,
    corporation_id: i32,
    page: Option<i32>,
    etag: Option<&str>,
  ) -> EsiResult<Response<Paged<Vec<CorporationMarketOrder>>>> {
    let result = get_authenticated_paged::<Vec<CorporationMarketOrder>>(
      access_token,
      &format!("characters/{}/orders", corporation_id),
      self,
      Some(
        vec![("page", page.unwrap_or(1).to_string())]
          .into_iter()
          .collect(),
      ),
      etag,
    )
    .await?;
    Ok(result)
  }

  /**
   * Get a list of historical orders in a corporation's orders
   * esi: https://esi.evetech.net/latest/corporations/{corporation_id}/orders/history/
   */
  pub async fn get_corporation_orders_history(
    &self,
    access_token: &str,
    corporation_id: i32,
    page: Option<i32>,
    etag: Option<&str>,
  ) -> EsiResult<Response<Paged<Vec<CorporationMarketOrder>>>> {
    let result = get_authenticated_paged::<Vec<CorporationMarketOrder>>(
      access_token,
      &format!("characters/{}/orders/history", corporation_id),
      self,
      Some(
        vec![("page", page.unwrap_or(1).to_string())]
          .into_iter()
          .collect(),
      ),
      etag,
    )
    .await?;
    Ok(result)
  }

  /**
   * Get a list of historical market statistics for a type in a region
   * esi: https://esi.evetech.net/latest/markets/{region_id}/history/
   */
  pub async fn get_market_history(
    &self,
    region_id: i32,
    type_id: i32,
    etag: Option<&str>,
  ) -> EsiResult<Response<Vec<MarketHistory>>> {
    let result = get_public::<Vec<MarketHistory>>(
      &format!("markets/{}/history", region_id),
      self,
      Some(vec![("type_id", type_id.to_string())].into_iter().collect()),
      etag,
    )
    .await?;
    Ok(result)
  }

  /**
   * Get a list of orders in a region
   * esi: https://esi.evetech.net/latest/markets/{region_id}/orders/
   */
  pub async fn get_market_orders(
    &self,
    region_id: i32,
    order_type: Option<&MarketOrderType>,
    type_id: Option<i32>,
    page: Option<i32>,
    etag: Option<&str>,
  ) -> EsiResult<Response<Paged<Vec<MarketOrder>>>> {
    let order_type = order_type.unwrap_or(&MarketOrderType::All).to_string();
    let mut params: HashMap<&str, String> = vec![
      ("order_type", order_type),
      ("page", page.unwrap_or(1).to_string()),
    ]
    .into_iter()
    .collect();
    if let Some(type_id) = type_id {
      let type_id = type_id.to_string();
      params.insert("type_id", type_id);
    }
    let result = get_public_paged::<Vec<MarketOrder>>(
      &format!("markets/{}/orders", region_id),
      self,
      Some(params),
      etag,
    )
    .await?;
    Ok(result)
  }

  /**
   * Get a list of market types for a region
   * esi: https://esi.evetech.net/latest/markets/{region_id}/types/
   */
  pub async fn get_market_types(
    &self,
    region_id: i32,
    page: Option<i32>,
    etag: Option<&str>,
  ) -> EsiResult<Response<Paged<Vec<i32>>>> {
    let page = page.unwrap_or(1);
    let result = get_public_paged::<Vec<i32>>(
      &format!("markets/{}/types", region_id),
      self,
      Some(vec![("page", page.to_string())].into_iter().collect()),
      etag,
    )
    .await?;
    Ok(result)
  }

  /**
   * Get a list of market groups
   * esi: https://esi.evetech.net/latest/markets/groups/
   */
  pub async fn get_market_groups(
    &self,
    etag: Option<&str>,
  ) -> EsiResult<Response<Paged<Vec<i32>>>> {
    let result =
      get_public_paged::<Vec<i32>>(&"markets/groups".to_string(), self, None, etag).await?;
    Ok(result)
  }

  /**
   * Get a list of types in a market group
   * esi: https://esi.evetech.net/latest/markets/groups/{market_group_id}/
   */
  pub async fn get_market_group(
    &self,
    market_group_id: i32,
    etag: Option<&str>,
  ) -> EsiResult<Response<MarketGroup>> {
    let result = get_public::<MarketGroup>(
      &format!("markets/groups/{}", market_group_id),
      self,
      None,
      etag,
    )
    .await?;
    Ok(result)
  }

  pub async fn get_market_prices(
    &self,
    etag: Option<&str>,
  ) -> EsiResult<Response<Vec<MarketPrice>>> {
    let result = get_public::<Vec<MarketPrice>>("markets/prices", self, None, etag).await?;
    Ok(result)
  }

  /**
   * Get a list of orders in a structure
   * esi: https://esi.evetech.net/latest/markets/structures/{structure_id}/
   */
  pub async fn get_market_structure(
    &self,
    access_token: &str,
    structure_id: i64,
    etag: Option<&str>,
  ) -> EsiResult<Response<Vec<MarketOrder>>> {
    let result = get_authenticated::<Vec<MarketOrder>>(
      access_token,
      &format!("markets/structures/{}", structure_id),
      self,
      None,
      etag,
    )
    .await?;
    Ok(result)
  }
}

#[cfg(test)]
mod tests {
  use crate::Esi;

  const OWNER_ID: &str = "1234567890";
  const CLIENT_ID: &str = "1234567890";
  const CLIENT_SECRET: &str = "1234567890";
  const CALLBACK_URL: &str = "https://example.com/callback";

  #[tokio::test]
  async fn test_get_market_history() {
    let esi = Esi::new(OWNER_ID, CLIENT_ID, CLIENT_SECRET, CALLBACK_URL, 10);
    assert!(esi.is_ok());
    let esi = esi.unwrap();
    let result = esi.get_market_history(10000002, 34, None).await;
    assert!(result.is_ok());
    let etag = result.unwrap().headers.etag;
    let result = esi.get_market_history(10000002, 34, Some(&etag)).await;
    assert!(result.is_err_and(|x| x.status() == 304));
  }

  #[tokio::test]
  async fn test_get_market_orders() {
    let esi = Esi::new(OWNER_ID, CLIENT_ID, CLIENT_SECRET, CALLBACK_URL, 10);
    assert!(esi.is_ok());
    let esi = esi.unwrap();
    let result = esi
      .get_market_orders(10000002, None, None, None, None)
      .await;
    assert!(result.is_ok());
  }

  #[tokio::test]
  async fn test_get_market_types() {
    let esi = Esi::new(OWNER_ID, CLIENT_ID, CLIENT_SECRET, CALLBACK_URL, 10);
    assert!(esi.is_ok());
    let esi = esi.unwrap();
    let result = esi.get_market_types(10000002, None, None).await;
    assert!(result.is_ok());
  }

  #[tokio::test]
  async fn test_get_market_groups() {
    let esi = Esi::new(OWNER_ID, CLIENT_ID, CLIENT_SECRET, CALLBACK_URL, 10);
    assert!(esi.is_ok());
    let esi = esi.unwrap();
    let result = esi.get_market_groups(None).await;
    assert!(result.is_ok());
  }

  #[tokio::test]
  async fn test_get_market_group() {
    let esi = Esi::new(OWNER_ID, CLIENT_ID, CLIENT_SECRET, CALLBACK_URL, 10);
    assert!(esi.is_ok());
    let esi = esi.unwrap();
    let result = esi.get_market_group(2, None).await;
    assert!(result.is_ok());
  }

  #[tokio::test]
  async fn test_get_market_prices() {
    let esi = Esi::new(OWNER_ID, CLIENT_ID, CLIENT_SECRET, CALLBACK_URL, 10);
    assert!(esi.is_ok());
    let esi = esi.unwrap();
    let result = esi.get_market_prices(None).await;
    assert!(result.is_ok());
  }
}
