use std::sync::Arc;

use crate::common::child_spec::ChildSpec;
use crate::common::factory::ActorFactory;
use crate::mixed::{ErasedActorFactory, MixedSup};

pub trait Append<K, F> {
    type Out;
    fn append(self, key: K, child_spec: ChildSpec<F>) -> Self::Out;
}

pub trait CollectInto<K, R> {
    fn collect_into(self, into: &mut impl Extend<(K, ChildSpec<ErasedActorFactory<R>>)>);
}

pub struct KV<K, R> {
    key:        K,
    child_spec: ChildSpec<ErasedActorFactory<R>>,
}

impl<K, F, T> Append<K, F> for T
where
    F: ActorFactory<Args = ()>,
{
    type Out = (Self, KV<K, F::Runnable>);

    fn append(self, key: K, child_spec: ChildSpec<F>) -> Self::Out {
        let child_spec = child_spec.map_launcher::<ErasedActorFactory<_>, _>(|f| Arc::pin(f));
        (self, KV { key, child_spec })
    }
}

impl<K, R> CollectInto<K, R> for () {
    fn collect_into(self, _into: &mut impl Extend<(K, ChildSpec<ErasedActorFactory<R>>)>) {}
}
impl<K, R> CollectInto<K, R> for KV<K, R> {
    fn collect_into(self, into: &mut impl Extend<(K, ChildSpec<ErasedActorFactory<R>>)>) {
        let Self { key, child_spec } = self;
        into.extend([(key, child_spec)]);
    }
}
impl<K, R, Le, Ri> CollectInto<K, R> for (Le, Ri)
where
    Le: CollectInto<K, R>,
    Ri: CollectInto<K, R>,
{
    fn collect_into(self, into: &mut impl Extend<(K, ChildSpec<ErasedActorFactory<R>>)>) {
        let (le, ri) = self;
        le.collect_into(into);
        ri.collect_into(into);
    }
}

impl<RS, C> MixedSup<RS, C> {
    pub fn with_child<K, F>(
        self,
        key: K,
        child_spec: ChildSpec<F>,
    ) -> MixedSup<RS, <C as Append<K, F>>::Out>
    where
        C: Append<K, F>,
    {
        let Self {
            restart_strategy,
            children,
        } = self;
        let children = children.append(key, child_spec);
        MixedSup {
            restart_strategy,
            children,
        }
    }
}

impl<RS, C> Clone for MixedSup<RS, C>
where
    RS: Clone,
    C: Clone,
{
    fn clone(&self) -> Self {
        Self {
            restart_strategy: self.restart_strategy.clone(),
            children:         self.children.clone(),
        }
    }
}
