//! Aspect chain — ordered execution of aspects around a method call.
//!
//! Aspects execute in order: each one wraps the next, forming a chain.
//! The innermost item is the actual method invocation.
//!
//! ```text
//! @authenticate → @authorize → @cache → @log → [method body]
//!     before          before      check    log
//!                                 hit? ──→ return cached
//!                                 miss? → [method body] → store → after-log
//! ```
//!
//! ## Security-by-default
//!
//! `AspectChain::with_default_auth()` prepends `AuthenticateAspect` to every
//! chain. Call this when building chains for non-`@public` functions.

use crate::{aspects::AuthenticateAspect, AopResult, Aspect, InvocationContext, InvocationResult, ProceedFn};
use std::sync::Arc;

/// An ordered chain of aspects applied to a single method.
pub struct AspectChain {
    aspects: Vec<Arc<dyn Aspect>>,
}

impl AspectChain {
    pub fn new() -> Self {
        Self { aspects: Vec::new() }
    }

    /// Add an aspect to the end of the chain.
    pub fn add(mut self, aspect: Arc<dyn Aspect>) -> Self {
        self.aspects.push(aspect);
        self
    }

    /// Number of aspects in this chain.
    pub fn len(&self) -> usize {
        self.aspects.len()
    }

    pub fn is_empty(&self) -> bool {
        self.aspects.is_empty()
    }

    /// Execute the chain around the given proceed function.
    ///
    /// Aspects run in order (left to right in the decorator list).
    /// Each aspect's `around` method receives the next aspect's `around`
    /// as the `proceed` function, forming a true onion model.
    pub fn execute(
        &self,
        ctx: InvocationContext,
        proceed: ProceedFn,
    ) -> AopResult<InvocationResult> {
        if self.aspects.is_empty() {
            return proceed(ctx);
        }
        self.run_aspect(0, ctx, proceed)
    }

    fn run_aspect(
        &self,
        index: usize,
        ctx: InvocationContext,
        final_proceed: ProceedFn,
    ) -> AopResult<InvocationResult> {
        if index >= self.aspects.len() {
            return final_proceed(ctx);
        }

        let aspect = self.aspects[index].clone();
        let remaining_aspects = self.aspects[index + 1..].to_vec();
        let final_proceed = Arc::new(final_proceed);

        let next: ProceedFn = Box::new(move |ctx: InvocationContext| {
            if remaining_aspects.is_empty() {
                return final_proceed(ctx);
            }

            // Build remaining chain recursively
            let sub_chain = AspectChain {
                aspects: remaining_aspects.clone(),
            };
            sub_chain.execute(ctx, {
                let fp = final_proceed.clone();
                Box::new(move |ctx| fp(ctx))
            })
        });

        aspect.around(ctx, &next)
    }

    /// Return the names of all aspects in this chain (in order).
    pub fn aspect_names(&self) -> Vec<&str> {
        self.aspects.iter().map(|a| a.name()).collect()
    }

    /// Prepend `AuthenticateAspect` to this chain.
    ///
    /// This is the mechanism for security-by-default: the framework calls
    /// `with_default_auth()` on every chain that does NOT have `@public`.
    ///
    /// Equivalent to `.add(Arc::new(AuthenticateAspect))` at position 0, but
    /// semantically explicit about what it means.
    pub fn with_default_auth(self) -> Self {
        let mut aspects = vec![Arc::new(AuthenticateAspect) as Arc<dyn Aspect>];
        aspects.extend(self.aspects);
        Self { aspects }
    }

    /// Returns `true` if the chain contains an `AuthenticateAspect`.
    pub fn has_auth(&self) -> bool {
        self.aspects.iter().any(|a| a.name() == "authenticate")
    }
}

impl Default for AspectChain {
    fn default() -> Self {
        Self::new()
    }
}
