//! Main library entry point for hvcg_iam_openapi_identity implementation.

#![allow(unused_imports)]

use async_trait::async_trait;
use futures::{future, Stream, StreamExt, TryFutureExt, TryStreamExt};
use hyper::server::conn::Http;
use hyper::service::Service;
use log::info;
#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use openssl::ssl::SslAcceptorBuilder;
use std::future::Future;
use std::marker::PhantomData;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll};
use swagger::{Has, XSpanIdString};
use swagger::auth::MakeAllowAllAuthenticator;
use swagger::EmptyContext;
use tokio::net::TcpListener;

#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};

use hvcg_iam_openapi_identity::models;

/// Builds an SSL implementation for Simple HTTPS from some hard-coded file names
pub async fn create(addr: &str, https: bool) {
    let addr = addr.parse().expect("Failed to parse bind address");

    let server = Server::new();

    let service = MakeService::new(server);

    let service = MakeAllowAllAuthenticator::new(service, "cosmo");

    let mut service =
        hvcg_iam_openapi_identity::server::context::MakeAddContext::<_, EmptyContext>::new(
            service
        );

    if https {
        #[cfg(any(target_os = "macos", target_os = "windows", target_os = "ios"))]
        {
            unimplemented!("SSL is not implemented for the examples on MacOS, Windows or iOS");
        }

        #[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "ios")))]
        {
            let mut ssl = SslAcceptor::mozilla_intermediate_v5(SslMethod::tls()).expect("Failed to create SSL Acceptor");

            // Server authentication
            ssl.set_private_key_file("examples/server-key.pem", SslFiletype::PEM).expect("Failed to set private key");
            ssl.set_certificate_chain_file("examples/server-chain.pem").expect("Failed to set cerificate chain");
            ssl.check_private_key().expect("Failed to check private key");

            let tls_acceptor = Arc::new(ssl.build());
            let mut tcp_listener = TcpListener::bind(&addr).await.unwrap();
            let mut incoming = tcp_listener.incoming();

            while let (Some(tcp), rest) = incoming.into_future().await {
                if let Ok(tcp) = tcp {
                    let addr = tcp.peer_addr().expect("Unable to get remote address");
                    let service = service.call(addr);
                    let tls_acceptor = Arc::clone(&tls_acceptor);

                    tokio::spawn(async move {
                        let tls = tokio_openssl::accept(&*tls_acceptor, tcp).await.map_err(|_| ())?;

                        let service = service.await.map_err(|_| ())?;

                        Http::new().serve_connection(tls, service).await.map_err(|_| ())
                    });
                }

                incoming = rest;
            }
        }
    } else {
        // Using HTTP
        hyper::server::Server::bind(&addr).serve(service).await.unwrap()
    }
}

#[derive(Copy, Clone)]
pub struct Server<C> {
    marker: PhantomData<C>,
}

impl<C> Server<C> {
    pub fn new() -> Self {
        Server{marker: PhantomData}
    }
}


use hvcg_iam_openapi_identity::{
    Api,
    ActivateUserResponse,
    CreateUserResponse,
    DeactivateUserResponse,
    UpdatePasswordResponse,
    QueryUserByIdResponse,
    QueryUsersResponse,
};
use hvcg_iam_openapi_identity::server::MakeService;
use std::error::Error;
use swagger::ApiError;

#[async_trait]
impl<C> Api<C> for Server<C> where C: Has<XSpanIdString> + Send + Sync
{
    /// Activate a user
    async fn activate_user(
        &self,
        inline_object2: models::InlineObject2,
        context: &C) -> Result<ActivateUserResponse, ApiError>
    {
        let context = context.clone();
        info!("activate_user({:?}) - X-Span-ID: {:?}", inline_object2, context.get().0.clone());
        Err("Generic failuare".into())
    }

    /// Create user
    async fn create_user(
        &self,
        user: models::User,
        context: &C) -> Result<CreateUserResponse, ApiError>
    {
        let context = context.clone();
        info!("create_user({:?}) - X-Span-ID: {:?}", user, context.get().0.clone());
        Err("Generic failuare".into())
    }

    /// Deactive user
    async fn deactivate_user(
        &self,
        inline_object1: models::InlineObject1,
        context: &C) -> Result<DeactivateUserResponse, ApiError>
    {
        let context = context.clone();
        info!("deactivate_user({:?}) - X-Span-ID: {:?}", inline_object1, context.get().0.clone());
        Err("Generic failuare".into())
    }

    /// update password
    async fn update_password(
        &self,
        id: uuid::Uuid,
        inline_object: models::InlineObject,
        context: &C) -> Result<UpdatePasswordResponse, ApiError>
    {
        let context = context.clone();
        info!("update_password({:?}, {:?}) - X-Span-ID: {:?}", id, inline_object, context.get().0.clone());
        Err("Generic failuare".into())
    }

    /// Get user infomation by id
    async fn query_user_by_id(
        &self,
        id: uuid::Uuid,
        context: &C) -> Result<QueryUserByIdResponse, ApiError>
    {
        let context = context.clone();
        info!("query_user_by_id({:?}) - X-Span-ID: {:?}", id, context.get().0.clone());
        Err("Generic failuare".into())
    }

    /// 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>
    {
        let context = context.clone();
        info!("query_users({:?}, {:?}, {:?}, {:?}, {:?}, {:?}) - X-Span-ID: {:?}", username, phone, email, enabled, offset, count, context.get().0.clone());
        Err("Generic failuare".into())
    }

}
