//! Attack and defeat relations between ASPIC+ arguments.
//!
//! This module defines the *kinds* of attack that one argument can make on
//! another, following Definition 3.1 of Modgil & Prakken (2014):
//!
//! | Variant                        | Target                                  |
//! |--------------------------------|-----------------------------------------|
//! | [`AttackKind::Rebut`]          | Conclusion of a defeasible top rule of a sub-argument |
//! | [`AttackKind::Undermine`]      | An ordinary premise of the target       |
//! | [`AttackKind::Undercut`]       | The name of a defeasible rule in the target |
//!
//! An [`Attack`] records who attacks whom and how. A [`Defeat`] is an
//! attack that survives the preference/ordering check — only defeats are
//! forwarded to the abstract argumentation framework.
//!
//! The algorithms that *compute* attacks live in
//! [`crate::pipeline::attack`]; the function that converts attacks into
//! defeats by applying the preference ordering is [`compute_defeats`].

use crate::framework::argument::ArgumentId;
use crate::framework::argument_store::ArgumentStore;
use crate::system::ordering::{ArgumentOrdering, Preference};
use crate::system::rules::RuleId;

/// The kind of attack one argument makes on another.
///
/// Concrete [`AttackComputer`](crate::pipeline::traits::AttackComputer)
/// implementations that produce these values live in
/// [`crate::pipeline::attack`].
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum AttackKind {
    /// A attacks the conclusion of a defeasible top rule of a sub-argument
    /// of B.
    ///
    /// `contrary` is `true` when the attack is via an asymmetric contrary
    /// relation (Definition 5.1, Modgil & Prakken 2014), in which case the
    /// resulting defeat bypasses the preference check.
    Rebut {
        target_sub: ArgumentId,
        contrary: bool,
    },
    /// A attacks an ordinary premise of B.
    ///
    /// `contrary` is `true` when the attack is via an asymmetric contrary
    /// relation, in which case the resulting defeat bypasses the preference
    /// check.
    Undermine {
        target_premise: ArgumentId,
        contrary: bool,
    },
    /// A's conclusion is the contrary of the name of a defeasible rule
    /// used in B.
    Undercut {
        target_sub: ArgumentId,
        rule_id: RuleId,
    },
}

/// A raw attack from one argument to another before the preference filter.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Attack {
    /// The argument making the attack.
    pub attacker: ArgumentId,
    /// The argument being attacked.
    pub target: ArgumentId,
    /// The type of attack (rebut, undermine, or undercut).
    pub kind: AttackKind,
}

/// An attack that has survived the preference/ordering check and will be
/// included in the abstract argumentation framework as a defeat relation.
#[derive(Clone, Debug)]
pub struct Defeat {
    /// The defeating argument.
    pub attacker: ArgumentId,
    /// The defeated argument.
    pub target: ArgumentId,
    /// The type of attack that produced this defeat.
    pub kind: AttackKind,
}

/// Compute defeats from attacks by applying the argument ordering
/// (Definition 3.12, Modgil & Prakken 2014).
///
/// For each attack, the preference check determines whether it succeeds:
/// - **Undercut**: always succeeds (the ordering is irrelevant).
/// - **Rebut / Undermine (contrary)**: always succeeds (asymmetric
///   contrary bypasses the preference check per §5).
/// - **Rebut / Undermine (contradictory)**: succeeds unless the attacker
///   is *strictly dispreferred* to the point of attack.
///
/// Exposed as a free function so callers can use it independently of
/// the pipeline (e.g. for AF export in benchmarking tools).
pub fn compute_defeats(
    attacks: &[Attack],
    store: &ArgumentStore,
    ordering: &dyn ArgumentOrdering,
) -> Vec<Defeat> {
    let mut defeats = Vec::new();

    for attack in attacks {
        let succeeds = match &attack.kind {
            AttackKind::Undercut { .. } => true,
            AttackKind::Rebut {
                target_sub,
                contrary,
            } => {
                if *contrary {
                    true
                } else {
                    let pref = ordering.compare(attack.attacker, *target_sub, store);
                    pref != Preference::StrictlyDispreferred
                }
            }
            AttackKind::Undermine {
                target_premise,
                contrary,
            } => {
                if *contrary {
                    true
                } else {
                    let pref = ordering.compare(attack.attacker, *target_premise, store);
                    pref != Preference::StrictlyDispreferred
                }
            }
        };

        if succeeds {
            defeats.push(Defeat {
                attacker: attack.attacker,
                target: attack.target,
                kind: attack.kind.clone(),
            });
        }
    }

    defeats
}
