//! Set-level preference derived from an argument ordering.
//!
//! Lifts an individual [`ArgumentOrdering`] to a [`SetPreference`] via
//! elitist or democratic set comparison (arXiv:1804.06763 Definition 19).
//! Used by the accrual evaluator when no explicit set preference is
//! provided.

use std::collections::BTreeSet;

use super::{ArgumentOrdering, LiftingStrategy, Preference, SetPreference};
use crate::framework::argument::ArgumentId;
use crate::framework::argument_store::ArgumentStore;

/// Derives set-level comparisons from an individual [`ArgumentOrdering`]
/// via one of the standard liftings (Elitist or Democratic).
pub struct LiftedSetPreference {
    pub base: Box<dyn ArgumentOrdering>,
    pub lifting: LiftingStrategy,
}

impl SetPreference for LiftedSetPreference {
    fn compare(
        &self,
        set_a: &BTreeSet<ArgumentId>,
        set_b: &BTreeSet<ArgumentId>,
        store: &ArgumentStore,
    ) -> Preference {
        match self.lifting {
            LiftingStrategy::Elitist => elitist_compare(set_a, set_b, &*self.base, store),
            LiftingStrategy::Democratic => democratic_compare(set_a, set_b, &*self.base, store),
        }
    }
}

// ---------------------------------------------------------------------------
// Comparison kernels — corrected per arXiv:1804.06763 Definition 19
// ---------------------------------------------------------------------------

/// Corrected Elitist set comparison (arXiv:1804.06763 Definition 19, Eq. 3).
///
/// Computes strict ◁ directly: `B ◁_Eli A` iff ∃b∈B: ∀a∈A: b < a.
/// Sets are equal iff they contain the same argument IDs.
/// Otherwise, if neither ◁ holds, the sets are incomparable.
pub(crate) fn elitist_compare(
    set_a: &BTreeSet<ArgumentId>,
    set_b: &BTreeSet<ArgumentId>,
    base: &dyn ArgumentOrdering,
    store: &ArgumentStore,
) -> Preference {
    if set_a == set_b {
        return Preference::Equal;
    }
    if set_a.is_empty() {
        return Preference::StrictlyDispreferred;
    }
    if set_b.is_empty() {
        return Preference::StrictlyPreferred;
    }

    let b_strict_a = set_b.iter().any(|&b| {
        set_a
            .iter()
            .all(|&a| base.compare(b, a, store).is_strictly_less())
    });

    let a_strict_b = set_a.iter().any(|&a| {
        set_b
            .iter()
            .all(|&b| base.compare(a, b, store).is_strictly_less())
    });

    match (b_strict_a, a_strict_b) {
        (true, false) => Preference::StrictlyPreferred,
        (false, true) => Preference::StrictlyDispreferred,
        _ => Preference::Incomparable,
    }
}

/// Corrected Democratic set comparison (arXiv:1804.06763 Definition 19, Eq. 3).
///
/// Computes strict ◁ directly: `B ◁_Dem A` iff ∀b∈B: ∃a∈A: b < a.
/// Sets are equal iff they contain the same argument IDs.
/// Otherwise, if neither ◁ holds, the sets are incomparable.
pub(crate) fn democratic_compare(
    set_a: &BTreeSet<ArgumentId>,
    set_b: &BTreeSet<ArgumentId>,
    base: &dyn ArgumentOrdering,
    store: &ArgumentStore,
) -> Preference {
    if set_a == set_b {
        return Preference::Equal;
    }
    if set_a.is_empty() {
        return Preference::StrictlyDispreferred;
    }
    if set_b.is_empty() {
        return Preference::StrictlyPreferred;
    }

    let b_strict_a = set_b.iter().all(|&b| {
        set_a
            .iter()
            .any(|&a| base.compare(b, a, store).is_strictly_less())
    });

    let a_strict_b = set_a.iter().all(|&a| {
        set_b
            .iter()
            .any(|&b| base.compare(a, b, store).is_strictly_less())
    });

    match (b_strict_a, a_strict_b) {
        (true, false) => Preference::StrictlyPreferred,
        (false, true) => Preference::StrictlyDispreferred,
        _ => Preference::Incomparable,
    }
}
