#![allow(missing_docs, trivial_casts, unused_variables, unused_mut, unused_imports, unused_extern_crates, non_camel_case_types)]

use async_trait::async_trait;
use futures::Stream;
use std::error::Error;
use std::task::{Poll, Context};
use swagger::{ApiError, ContextWrapper};
use serde::{Serialize, Deserialize};

type ServiceError = Box<dyn Error + Send + Sync + 'static>;

pub const BASE_PATH: &'static str = "";
pub const API_VERSION: &'static str = "0.0.7";

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
pub enum ActivateUserResponse {
    /// successful operation
    SuccessfulOperation
    (models::User)
    ,
    /// Invalid input
    InvalidInput
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
pub enum CreateUserResponse {
    /// successful operation
    SuccessfulOperation
    (models::User)
    ,
    /// Invalid input
    InvalidInput
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
pub enum DeactivateUserResponse {
    /// successful operation
    SuccessfulOperation
    (models::User)
    ,
    /// Invalid input
    InvalidInput
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
pub enum UpdatePasswordResponse {
    /// successful operation
    SuccessfulOperation
    (models::User)
    ,
    /// Invalid input
    InvalidInput
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
pub enum QueryUserByIdResponse {
    /// successful operation
    SuccessfulOperation
    (models::User)
    ,
    /// Invalid ID supplied
    InvalidIDSupplied
    ,
    /// User not found
    UserNotFound
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[must_use]
pub enum QueryUsersResponse {
    /// successful operation
    SuccessfulOperation
    (models::UserCollection)
    ,
    /// Invalid ID supplied
    InvalidIDSupplied
    ,
    /// User not found
    UserNotFound
}

/// API
#[async_trait]
pub trait Api<C: Send + Sync> {
    fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>> {
        Poll::Ready(Ok(()))
    }

    /// Activate a user
    async fn activate_user(
        &self,
        inline_object2: models::InlineObject2,
        context: &C) -> Result<ActivateUserResponse, ApiError>;

    /// Create user
    async fn create_user(
        &self,
        user: models::User,
        context: &C) -> Result<CreateUserResponse, ApiError>;

    /// Deactive user
    async fn deactivate_user(
        &self,
        inline_object1: models::InlineObject1,
        context: &C) -> Result<DeactivateUserResponse, ApiError>;

    /// update password
    async fn update_password(
        &self,
        id: uuid::Uuid,
        inline_object: models::InlineObject,
        context: &C) -> Result<UpdatePasswordResponse, ApiError>;

    /// Get user infomation by id
    async fn query_user_by_id(
        &self,
        id: uuid::Uuid,
        context: &C) -> Result<QueryUserByIdResponse, ApiError>;

    /// Get users infomation
    async fn query_users(
        &self,
        username: Option<String>,
        phone: Option<String>,
        email: Option<String>,
        enabled: Option<bool>,
        offset: Option<i32>,
        count: Option<i32>,
        context: &C) -> Result<QueryUsersResponse, ApiError>;

}

/// API where `Context` isn't passed on every API call
#[async_trait]
pub trait ApiNoContext<C: Send + Sync> {

    fn poll_ready(&self, _cx: &mut Context) -> Poll<Result<(), Box<dyn Error + Send + Sync + 'static>>>;

    fn context(&self) -> &C;

    /// Activate a user
    async fn activate_user(
        &self,
        inline_object2: models::InlineObject2,
        ) -> Result<ActivateUserResponse, ApiError>;

    /// Create user
    async fn create_user(
        &self,
        user: models::User,
        ) -> Result<CreateUserResponse, ApiError>;

    /// Deactive user
    async fn deactivate_user(
        &self,
        inline_object1: models::InlineObject1,
        ) -> Result<DeactivateUserResponse, ApiError>;

    /// update password
    async fn update_password(
        &self,
        id: uuid::Uuid,
        inline_object: models::InlineObject,
        ) -> Result<UpdatePasswordResponse, ApiError>;

    /// Get user infomation by id
    async fn query_user_by_id(
        &self,
        id: uuid::Uuid,
        ) -> Result<QueryUserByIdResponse, ApiError>;

    /// Get users infomation
    async fn query_users(
        &self,
        username: Option<String>,
        phone: Option<String>,
        email: Option<String>,
        enabled: Option<bool>,
        offset: Option<i32>,
        count: Option<i32>,
        ) -> Result<QueryUsersResponse, ApiError>;

}

/// Trait to extend an API to make it easy to bind it to a context.
pub trait ContextWrapperExt<C: Send + Sync> where Self: Sized
{
    /// Binds this API to a context.
    fn with_context(self: Self, context: C) -> ContextWrapper<Self, C>;
}

impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ContextWrapperExt<C> for T {
    fn with_context(self: T, context: C) -> ContextWrapper<T, C> {
         ContextWrapper::<T, C>::new(self, context)
    }
}

#[async_trait]
impl<T: Api<C> + Send + Sync, C: Clone + Send + Sync> ApiNoContext<C> for ContextWrapper<T, C> {
    fn poll_ready(&self, cx: &mut Context) -> Poll<Result<(), ServiceError>> {
        self.api().poll_ready(cx)
    }

    fn context(&self) -> &C {
        ContextWrapper::context(self)
    }

    /// Activate a user
    async fn activate_user(
        &self,
        inline_object2: models::InlineObject2,
        ) -> Result<ActivateUserResponse, ApiError>
    {
        let context = self.context().clone();
        self.api().activate_user(inline_object2, &context).await
    }

    /// Create user
    async fn create_user(
        &self,
        user: models::User,
        ) -> Result<CreateUserResponse, ApiError>
    {
        let context = self.context().clone();
        self.api().create_user(user, &context).await
    }

    /// Deactive user
    async fn deactivate_user(
        &self,
        inline_object1: models::InlineObject1,
        ) -> Result<DeactivateUserResponse, ApiError>
    {
        let context = self.context().clone();
        self.api().deactivate_user(inline_object1, &context).await
    }

    /// update password
    async fn update_password(
        &self,
        id: uuid::Uuid,
        inline_object: models::InlineObject,
        ) -> Result<UpdatePasswordResponse, ApiError>
    {
        let context = self.context().clone();
        self.api().update_password(id, inline_object, &context).await
    }

    /// Get user infomation by id
    async fn query_user_by_id(
        &self,
        id: uuid::Uuid,
        ) -> Result<QueryUserByIdResponse, ApiError>
    {
        let context = self.context().clone();
        self.api().query_user_by_id(id, &context).await
    }

    /// Get users infomation
    async fn query_users(
        &self,
        username: Option<String>,
        phone: Option<String>,
        email: Option<String>,
        enabled: Option<bool>,
        offset: Option<i32>,
        count: Option<i32>,
        ) -> Result<QueryUsersResponse, ApiError>
    {
        let context = self.context().clone();
        self.api().query_users(username, phone, email, enabled, offset, count, &context).await
    }

}


#[cfg(feature = "client")]
pub mod client;

// Re-export Client as a top-level name
#[cfg(feature = "client")]
pub use client::Client;

#[cfg(feature = "server")]
pub mod server;

// Re-export router() as a top-level name
#[cfg(feature = "server")]
pub use self::server::Service;

#[cfg(feature = "server")]
pub mod context;

pub mod models;

#[cfg(any(feature = "client", feature = "server"))]
pub(crate) mod header;
