// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use super::ForkByErrorPredicate;
use alloc::vec::Vec;
#[cfg(feature = "datagen")]
use icu_provider::datagen;
use icu_provider::prelude::*;

/// A provider that returns data from one of two child providers based on a predicate function.
///
/// This is an abstract forking provider that must be provided with a type implementing the
/// [`ForkByErrorPredicate`] trait.
///
/// [`ForkByErrorProvider`] does not support forking between [`DataProvider`]s. However, it
/// supports forking between [`AnyProvider`], [`BufferProvider`], and [`DynamicDataProvider`].
#[derive(Debug, PartialEq, Eq)]
pub struct ForkByErrorProvider<P0, P1, F>(P0, P1, F);

impl<P0, P1, F> ForkByErrorProvider<P0, P1, F> {
    /// Create a new provider that forks between the two children.
    ///
    /// The `predicate` argument should be an instance of a struct implementing
    /// [`ForkByErrorPredicate`].
    pub fn new_with_predicate(p0: P0, p1: P1, predicate: F) -> Self {
        Self(p0, p1, predicate)
    }

    /// Returns references to the inner providers.
    pub fn inner(&self) -> (&P0, &P1) {
        (&self.0, &self.1)
    }

    /// Returns mutable references to the inner providers.
    pub fn inner_mut(&mut self) -> (&mut P0, &mut P1) {
        (&mut self.0, &mut self.1)
    }

    /// Returns ownership of the inner providers to the caller.
    pub fn into_inner(self) -> (P0, P1) {
        (self.0, self.1)
    }
}

impl<P0, P1, F> BufferProvider for ForkByErrorProvider<P0, P1, F>
where
    P0: BufferProvider,
    P1: BufferProvider,
    F: ForkByErrorPredicate,
{
    fn load_buffer(
        &self,
        key: DataKey,
        req: DataRequest,
    ) -> Result<DataResponse<BufferMarker>, DataError> {
        let result = self.0.load_buffer(key, req);
        match result {
            Ok(ok) => return Ok(ok),
            Err(err) if !self.2.test(key, Some(req), err) => return Err(err),
            _ => (),
        };
        self.1.load_buffer(key, req)
    }
}

impl<P0, P1, F> AnyProvider for ForkByErrorProvider<P0, P1, F>
where
    P0: AnyProvider,
    P1: AnyProvider,
    F: ForkByErrorPredicate,
{
    fn load_any(&self, key: DataKey, req: DataRequest) -> Result<AnyResponse, DataError> {
        let result = self.0.load_any(key, req);
        match result {
            Ok(ok) => return Ok(ok),
            Err(err) if !self.2.test(key, Some(req), err) => return Err(err),
            _ => (),
        };
        self.1.load_any(key, req)
    }
}

impl<M, P0, P1, F> DynamicDataProvider<M> for ForkByErrorProvider<P0, P1, F>
where
    M: DataMarker,
    P0: DynamicDataProvider<M>,
    P1: DynamicDataProvider<M>,
    F: ForkByErrorPredicate,
{
    fn load_data(&self, key: DataKey, req: DataRequest) -> Result<DataResponse<M>, DataError> {
        let result = self.0.load_data(key, req);
        match result {
            Ok(ok) => return Ok(ok),
            Err(err) if !self.2.test(key, Some(req), err) => return Err(err),
            _ => (),
        };
        self.1.load_data(key, req)
    }
}

#[cfg(feature = "datagen")]
impl<M, P0, P1, F> datagen::IterableDynamicDataProvider<M> for ForkByErrorProvider<P0, P1, F>
where
    M: DataMarker,
    P0: datagen::IterableDynamicDataProvider<M>,
    P1: datagen::IterableDynamicDataProvider<M>,
    F: ForkByErrorPredicate,
{
    fn supported_locales_for_key(&self, key: DataKey) -> Result<Vec<DataLocale>, DataError> {
        let result = self.0.supported_locales_for_key(key);
        match result {
            Ok(ok) => return Ok(ok),
            Err(err) if !self.2.test(key, None, err) => return Err(err),
            _ => (),
        };
        self.1.supported_locales_for_key(key)
    }
}

/// A provider that returns data from the first child provider passing a predicate function.
///
/// This is an abstract forking provider that must be provided with a type implementing the
/// [`ForkByErrorPredicate`] trait.
///
/// [`MultiForkByErrorProvider`] does not support forking between [`DataProvider`]s. However, it
/// supports forking between [`AnyProvider`], [`BufferProvider`], and [`DynamicDataProvider`].
#[derive(Debug)]
pub struct MultiForkByErrorProvider<P, F> {
    providers: Vec<P>,
    predicate: F,
}

impl<P, F> MultiForkByErrorProvider<P, F> {
    /// Create a new provider that forks between the vector of children.
    ///
    /// The `predicate` argument should be an instance of a struct implementing
    /// [`ForkByErrorPredicate`].
    pub fn new_with_predicate(providers: Vec<P>, predicate: F) -> Self {
        Self {
            providers,
            predicate,
        }
    }

    /// Returns a slice of the inner providers.
    pub fn inner(&self) -> &[P] {
        &self.providers
    }

    /// Exposes a mutable vector of providers to a closure so it can be mutated.
    pub fn with_inner_mut(&mut self, f: impl FnOnce(&mut Vec<P>)) {
        f(&mut self.providers)
    }

    /// Returns ownership of the inner providers to the caller.
    pub fn into_inner(self) -> Vec<P> {
        self.providers
    }

    /// Adds an additional child provider.
    pub fn push(&mut self, provider: P) {
        self.providers.push(provider);
    }
}

impl<P, F> BufferProvider for MultiForkByErrorProvider<P, F>
where
    P: BufferProvider,
    F: ForkByErrorPredicate,
{
    fn load_buffer(
        &self,
        key: DataKey,
        req: DataRequest,
    ) -> Result<DataResponse<BufferMarker>, DataError> {
        let mut last_error = F::UNIT_ERROR.with_key(key);
        for provider in self.providers.iter() {
            let result = provider.load_buffer(key, req);
            match result {
                Ok(ok) => return Ok(ok),
                Err(err) if !self.predicate.test(key, Some(req), err) => return Err(err),
                Err(err) => last_error = err,
            };
        }
        Err(last_error)
    }
}

impl<P, F> AnyProvider for MultiForkByErrorProvider<P, F>
where
    P: AnyProvider,
    F: ForkByErrorPredicate,
{
    fn load_any(&self, key: DataKey, req: DataRequest) -> Result<AnyResponse, DataError> {
        let mut last_error = F::UNIT_ERROR.with_key(key);
        for provider in self.providers.iter() {
            let result = provider.load_any(key, req);
            match result {
                Ok(ok) => return Ok(ok),
                Err(err) if !self.predicate.test(key, Some(req), err) => return Err(err),
                Err(err) => last_error = err,
            };
        }
        Err(last_error)
    }
}

impl<M, P, F> DynamicDataProvider<M> for MultiForkByErrorProvider<P, F>
where
    M: DataMarker,
    P: DynamicDataProvider<M>,
    F: ForkByErrorPredicate,
{
    fn load_data(&self, key: DataKey, req: DataRequest) -> Result<DataResponse<M>, DataError> {
        let mut last_error = F::UNIT_ERROR.with_key(key);
        for provider in self.providers.iter() {
            let result = provider.load_data(key, req);
            match result {
                Ok(ok) => return Ok(ok),
                Err(err) if !self.predicate.test(key, Some(req), err) => return Err(err),
                Err(err) => last_error = err,
            };
        }
        Err(last_error)
    }
}

#[cfg(feature = "datagen")]
impl<M, P, F> datagen::IterableDynamicDataProvider<M> for MultiForkByErrorProvider<P, F>
where
    M: DataMarker,
    P: datagen::IterableDynamicDataProvider<M>,
    F: ForkByErrorPredicate,
{
    fn supported_locales_for_key(&self, key: DataKey) -> Result<Vec<DataLocale>, DataError> {
        let mut last_error = F::UNIT_ERROR.with_key(key);
        for provider in self.providers.iter() {
            let result = provider.supported_locales_for_key(key);
            match result {
                Ok(ok) => return Ok(ok),
                Err(err) if !self.predicate.test(key, None, err) => return Err(err),
                Err(err) => last_error = err,
            };
        }
        Err(last_error)
    }
}

#[cfg(feature = "datagen")]
impl<P, MFrom, MTo, F> datagen::DataConverter<MFrom, MTo> for MultiForkByErrorProvider<P, F>
where
    P: datagen::DataConverter<MFrom, MTo>,
    F: ForkByErrorPredicate,
    MFrom: DataMarker,
    MTo: DataMarker,
{
    fn convert(
        &self,
        key: DataKey,
        mut from: DataPayload<MFrom>,
    ) -> Result<DataPayload<MTo>, (DataPayload<MFrom>, DataError)> {
        let mut last_error = F::UNIT_ERROR.with_key(key);
        for provider in self.providers.iter() {
            let result = provider.convert(key, from);
            match result {
                Ok(ok) => return Ok(ok),
                Err(e) => {
                    let (returned, err) = e;
                    if !self.predicate.test(key, None, err) {
                        return Err((returned, err));
                    }
                    from = returned;
                    last_error = err;
                }
            };
        }
        Err((from, last_error))
    }
}

#[cfg(feature = "datagen")]
impl<P0, P1, F, MFrom, MTo> datagen::DataConverter<MFrom, MTo> for ForkByErrorProvider<P0, P1, F>
where
    P0: datagen::DataConverter<MFrom, MTo>,
    P1: datagen::DataConverter<MFrom, MTo>,
    F: ForkByErrorPredicate,
    MFrom: DataMarker,
    MTo: DataMarker,
{
    fn convert(
        &self,
        key: DataKey,
        mut from: DataPayload<MFrom>,
    ) -> Result<DataPayload<MTo>, (DataPayload<MFrom>, DataError)> {
        let result = self.0.convert(key, from);
        match result {
            Ok(ok) => return Ok(ok),
            Err(e) => {
                let (returned, err) = e;
                if !self.2.test(key, None, err) {
                    return Err((returned, err));
                }
                from = returned;
            }
        };
        self.1.convert(key, from)
    }
}
