//! Classical ASPIC+ evaluator (Modgil & Prakken 2014).
//!
//! This module provides the standard evaluation pathway:
//!
//! 1. **Defeat computation** — apply the argument ordering to filter attacks
//!    into defeats ([`compute_defeats`]).
//! 2. **Abstract framework construction** — build an [`AbstractFramework`]
//!    from the defeats.
//! 3. **Semantics solving** — delegate to a [`SemanticsSolver`] backend
//!    to enumerate extensions and labellings.
//!
//! The [`ClassicalEvaluator`] implements the [`Evaluator`] trait and wraps
//! any [`SemanticsSolver`]. The built-in
//! [`NaiveEnumerationSolver`](super::naive_solver::NaiveEnumerationSolver)
//! uses subset enumeration; feature-gated alternatives (SAT, SCC, external
//! solvers) are in sibling modules.

use crate::framework::abstract_af::AbstractFramework;
use crate::framework::argument_store::ArgumentStore;
use crate::framework::attack::{compute_defeats, Attack};
use crate::framework::semantics::{Labelling, Semantics};
use crate::pipeline::traits::{EvaluationResult, Evaluator, PreferenceConfig, SemanticsSolver};

/// Standard ASPIC+ evaluator: defeats + abstract framework + solver.
///
/// Delegates the final semantics computation to a pluggable
/// [`SemanticsSolver`] backend, making it easy to swap between naive
/// enumeration, SAT-based solving, or external tools without changing
/// the defeat-computation logic.
pub struct ClassicalEvaluator {
    pub semantics_solver: Box<dyn SemanticsSolver>,
}

impl ClassicalEvaluator {
    fn build_framework(
        &self,
        store: &ArgumentStore,
        attacks: &[Attack],
        preference: &PreferenceConfig,
    ) -> Result<AbstractFramework, crate::error::AspicError> {
        let ordering = preference.as_argument_ordering().ok_or_else(|| {
            crate::error::AspicError::from(crate::error::SolverError::UnsupportedSemantics(
                "ClassicalEvaluator requires an ArgumentOrdering, not a SetPreference".into(),
            ))
        })?;
        let defeats = compute_defeats(attacks, store, ordering);
        Ok(AbstractFramework::from_defeats(store, &defeats))
    }
}

impl Evaluator for ClassicalEvaluator {
    fn evaluate(
        &self,
        store: &ArgumentStore,
        attacks: &[Attack],
        semantics: Semantics,
        preference: &PreferenceConfig,
    ) -> Result<EvaluationResult, crate::error::AspicError> {
        let framework = self.build_framework(store, attacks, preference)?;
        let extensions = self.semantics_solver.extensions(&framework, semantics);
        let labellings = extensions
            .iter()
            .map(|ext| Labelling::from_extension(ext, &framework))
            .collect();
        Ok(EvaluationResult {
            labellings,
            extensions,
        })
    }

    fn find_one(
        &self,
        store: &ArgumentStore,
        attacks: &[Attack],
        semantics: Semantics,
        preference: &PreferenceConfig,
    ) -> Result<EvaluationResult, crate::error::AspicError> {
        let framework = self.build_framework(store, attacks, preference)?;
        let ext = self.semantics_solver.find_one(&framework, semantics);
        let labellings = ext
            .iter()
            .map(|e| Labelling::from_extension(e, &framework))
            .collect();
        let extensions = ext.into_iter().collect();
        Ok(EvaluationResult {
            labellings,
            extensions,
        })
    }

    fn count(
        &self,
        store: &ArgumentStore,
        attacks: &[Attack],
        semantics: Semantics,
        preference: &PreferenceConfig,
    ) -> Result<usize, crate::error::AspicError> {
        let framework = self.build_framework(store, attacks, preference)?;
        Ok(self.semantics_solver.count(&framework, semantics))
    }
}
