// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use crate::{ResultExt as TokenManagerResultExt, TokenManagerError};
use anyhow::{format_err, Context as _};
use fidl::endpoints::{create_endpoints, ClientEnd, DiscoverableService, ServerEnd};
use fidl_fuchsia_auth::{AuthProviderConfig, Status};
use fidl_fuchsia_identity_external::{OauthMarker, OauthOpenIdConnectMarker, OpenIdConnectMarker};
use fuchsia_component::client::{launch, launcher, App};
use log::info;
use parking_lot::Mutex;
use std::sync::Arc;

/// A type capable of launching and connecting to a component that implements the
/// `AuthProvider` protocol. Launching is performed on demand.
///
/// Note: This type is not used by TokenManager directly, but is helpful for clients needing to
/// implement the `AuthProviderSupplier` trait.
pub struct AuthProviderConnection {
    /// The URL that should be used to launch the component.
    component_url: String,
    /// Optional params to be passed to the component at launch.
    params: Option<Vec<String>>,
    /// The state needed to retain a connection, once one has been created.
    connection_state: Mutex<Option<Arc<App>>>,
}

impl AuthProviderConnection {
    /// Creates a new `AuthProviderConnection` from the supplied `AuthProviderConfig`.
    pub fn from_config(config: AuthProviderConfig) -> Self {
        AuthProviderConnection {
            component_url: config.url,
            params: config.params,
            connection_state: Mutex::new(None),
        }
    }

    /// Creates a new `AuthProviderClient` from the supplied `AuthProviderConfig` reference.
    pub fn from_config_ref(config: &AuthProviderConfig) -> Self {
        AuthProviderConnection {
            component_url: config.url.clone(),
            params: config.params.clone(),
            connection_state: Mutex::new(None),
        }
    }

    /// Returns the component_url supplied at construction
    pub fn component_url(&self) -> &str {
        &self.component_url
    }

    /// Returns an `App` for opening new `AuthProvider` connections. If a
    /// component has previously been launched this is used, otherwise a fresh
    /// component is launched and its proxy is stored for future use.
    fn get_app(&self) -> Result<Arc<App>, TokenManagerError> {
        // If a factory proxy has already been created return it.
        let mut connection_state_lock = self.connection_state.lock();
        if let Some(connection_state) = &*connection_state_lock {
            return Ok(Arc::clone(&connection_state));
        }

        // Launch the auth provider and connect to its factory interface.
        info!("Launching AuthProvider component: {}", self.component_url);
        let launcher = launcher()
            .context("Failed to start launcher")
            .token_manager_status(Status::UnknownError)?;
        let app = launch(&launcher, self.component_url.clone(), self.params.clone())
            .context("Failed to launch AuthProvider")
            .token_manager_status(Status::AuthProviderServiceUnavailable)?;
        let app_arc = Arc::new(app);
        connection_state_lock.get_or_insert(Arc::clone(&app_arc));
        Ok(app_arc)
    }

    /// Connects the supplied channel to the `AuthProvider`. If a component has previously been
    /// launched this is used, otherwise a fresh component is launched.
    pub fn connect<S>(&self, server_end: ServerEnd<S>) -> Result<(), TokenManagerError>
    where
        S: AuthProviderService,
    {
        let app = self.get_app()?;
        app.pass_to_service::<S>(server_end.into_channel()).map_err(|err| {
            TokenManagerError::new(Status::AuthProviderServiceUnavailable)
                .with_cause(format_err!("GetAuthProvider method failed with {:?}", err))
        })
    }

    /// Returns a `ClientEnd` for communicating with the `AuthProvider`. If a component has
    /// previously been launched this is used, otherwise a fresh component is launched.
    pub fn get<S>(&self) -> Result<ClientEnd<S>, TokenManagerError>
    where
        S: AuthProviderService,
    {
        let (client_end, server_end) =
            create_endpoints().token_manager_status(Status::UnknownError)?;
        self.connect(server_end)?;
        Ok(client_end)
    }
}

/// A marker trait for identifying services that may be provided by an auth provider.
pub trait AuthProviderService: DiscoverableService {}

impl AuthProviderService for OauthMarker {}
impl AuthProviderService for OpenIdConnectMarker {}
impl AuthProviderService for OauthOpenIdConnectMarker {}
