use std::cmp::Ordering;
use std::marker::PhantomData;
use rand::{Rng, rng};
use amfiteatr_core::agent::{InformationSet, Policy};
use amfiteatr_core::error::AmfiteatrError;
use crate::scheme::{ClassicAction, ClassicScheme, ClassicGameError, UsizeAgentId};
use crate::scheme::ClassicAction::{Down, Up};

/// Classic pure strategy - allways one specified action from [`ClassicAction`].
pub struct ClassicPureStrategy<ID: UsizeAgentId, IS: InformationSet<ClassicScheme<ID>>>{
    pub action: ClassicAction,
    _is: PhantomData<IS>,
    _id: PhantomData<ID>,
}

impl<ID: UsizeAgentId, IS: InformationSet<ClassicScheme<ID>>> ClassicPureStrategy<ID, IS>{
    pub fn new(action: ClassicAction) -> Self{
        Self{
            action,
            _is: Default::default(),
            _id: Default::default()
        }
    }



}
impl<ID: UsizeAgentId, IS: InformationSet<ClassicScheme<ID>>> Policy<ClassicScheme<ID>> for ClassicPureStrategy<ID, IS>{
    type InfoSetType = IS ;

    fn select_action(&self, _state: &Self::InfoSetType) -> Result<ClassicAction, AmfiteatrError<ClassicScheme<ID>>> {
        Ok(self.action)
    }
}
/// Selects action [`Up`] with given probability, otherwise [`Down`].
pub struct ClassicMixedStrategy<ID: UsizeAgentId, IS: InformationSet<ClassicScheme<ID>>>{
    probability_up: f64,
    _is: PhantomData<IS>,
    _id: PhantomData<ID>,
}

impl<ID: UsizeAgentId, IS: InformationSet<ClassicScheme<ID>>> ClassicMixedStrategy<ID, IS>{
    pub fn new(probability_up: f64) -> Self{
        Self{
            probability_up,
            _is: Default::default(),
            _id: Default::default(),
        }
    }
    pub fn new_checked(probability: f64) -> Result<Self, ClassicGameError<ID>>{
        if !(0.0..=1.0).contains(&probability){
            Err(ClassicGameError::NotAProbability(probability))
        } else{
            Ok(Self::new(probability))
        }
    }
}

impl<ID: UsizeAgentId, IS: InformationSet<ClassicScheme<ID>>> Policy<ClassicScheme<ID>> for ClassicMixedStrategy<ID, IS>{
    type InfoSetType = IS ;

    fn select_action(&self, _state: &Self::InfoSetType) -> Result<ClassicAction, AmfiteatrError<ClassicScheme<ID>>> {
        let mut rng = rng();
        let sample = rng.random_range(0.0..1.0);
        sample.partial_cmp(&self.probability_up).map(|o|{
          match o{
              Ordering::Less => Up,
              Ordering::Equal => Down,
              Ordering::Greater => Down,
          }
        }).ok_or_else(||AmfiteatrError::NoActionAvailable {
            context: "ClassicMixedStrategy".to_string(),
        })

    }
}

