use serde::{Deserialize, Serialize};
use std::fmt;
// use rand::{rng, seq::IndexedRandom};
// use strum_macros::EnumIter;

/// Module defines the structure and data for the course units, including lessons and modules.
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Hash, Clone)]
pub(crate) enum Module {
    Introduction,
    Logic(Logic),
    Fallacy(Fallacy),
    Bias(Bias),
    Appraisal(Appraisal),
}
impl Default for Module {
    fn default() -> Self {
        Module::Introduction
    }
}
impl fmt::Display for Module {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Module::Introduction => write!(f, "Introduction"),
            Module::Logic(logic) => write!(f, "Logic: {}", logic),
            Module::Fallacy(fallacy) => write!(f, "Fallacy: {}", fallacy),
            Module::Bias(bias) => write!(f, "Bias: {}", bias),
            Module::Appraisal(appraisal) => write!(f, "Appraisal: {}", appraisal),
        }
    }
}
impl Module {
    pub(crate) fn get_filename(&self) -> String {
        match self {
            Module::Introduction => format!("data/introduction.json"),
            Module::Logic(logic) => format!("data/logic/{}.json", logic.get_filename()),
            Module::Fallacy(fallacy) => format!("data/fallacy/{}.json", fallacy.get_filename()),
            Module::Bias(bias) => format!("data/bias/{}.json", bias.get_filename()),
            Module::Appraisal(appraisal) => format!("data/appraisal/{}.json", appraisal.get_filename()),
        }
    }
    // pub(crate) fn random() -> Self {
    //     let mut rng = rng();
    //     let variants = [
    //         Module::Logic(Logic::random()),
    //         Module::Fallacy(Fallacy::random()),
    //         Module::Bias(Bias::random()),
    //         Module::Appraisal(Appraisal::random()),
    //     ];
    //     variants.choose(&mut rng).unwrap().clone()
    // }
    pub fn iter() -> impl Iterator<Item = Self> {
        vec![
            Module::Introduction,
            Module::Logic(Logic::LogicalOperations),
            Module::Logic(Logic::BooleanAlgebra),
            Module::Fallacy(Fallacy::FormalFallacy(FormalFallacy::PropositionalFallacy)),
            Module::Fallacy(Fallacy::FormalFallacy(FormalFallacy::ProbabilisticFallacy)),
            Module::Fallacy(Fallacy::FormalFallacy(FormalFallacy::SyllogisticFallacy)),
            Module::Fallacy(Fallacy::FormalFallacy(FormalFallacy::QuantificationalFallacy)),
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::PostHocErgoPropterHoc)),
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::SlipperySlope)),
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::TexasSharpshooter)),
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::HastyGeneralization)),
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::OverGeneralization)),
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::NoTrueScotsman)),
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::QuotingOutOfContext)),
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::AdHominem)),
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::TuQuoque)),
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::Bandwagon)),
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::StrawMan)),
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::AdIgnorantiam)),
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::SpecialPleading)),
            Module::Fallacy(Fallacy::InformalFallacy(InformalFallacy::BeggingTheQuestion)),
            Module::Bias(Bias::ConfirmationBias),
            Module::Bias(Bias::TheHaloEffect),
            Module::Bias(Bias::FundamentalAttributionError),
            Module::Bias(Bias::InGroupBias),
            Module::Bias(Bias::DunningKrugerEffect),
            Module::Bias(Bias::BarnumEffect),
            Module::Appraisal(Appraisal::ConversionToPropositional),
            Module::Appraisal(Appraisal::CounterArgument),
        ]
        .into_iter()
    }
    // pub(crate) fn create_empty_files() -> std::io::Result<()> {

    //     for module in Module::iter() {
    //         let filename = module.get_filename();
    //         let path = Path::new(&filename);
    //         if let Some(parent) = path.parent() {
    //             fs::create_dir_all(parent)?;
    //         }
    //         fs::File::create(path)?;
    //     }
    //     Ok(())
    // }
}


/// Logic module, which includes various lessons related to logical reasoning and operations.
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub(crate) enum Logic {
    LogicalOperations,
    BooleanAlgebra,
}
impl Default for Logic {
    fn default() -> Self {
        Logic::LogicalOperations
    }
}
impl std::fmt::Display for Logic {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Logic::LogicalOperations => write!(f, "Logical Operations"),
            Logic::BooleanAlgebra => write!(f, "Boolean Algebra"),
        }
    }
}
impl Logic {
    pub(crate) fn get_filename(&self) -> String {
        match self {
            Logic::LogicalOperations => "logical_operations".to_string(),
            Logic::BooleanAlgebra => "boolean_algebra".to_string(),
        }
    }
    // pub(crate) fn random() -> Self {
    //     let mut rng = rng();
    //     let variants = [Logic::LogicalOperations, Logic::BooleanAlgebra];
    //     variants.choose(&mut rng).unwrap().clone()
    // }
}

/// Fallacy module, which includes various lessons related to logical fallacies and errors in reasoning.
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub(crate) enum Fallacy {
    FormalFallacy(FormalFallacy),
    InformalFallacy(InformalFallacy),
}
impl Default for Fallacy {
    fn default() -> Self {
        Fallacy::FormalFallacy(FormalFallacy::PropositionalFallacy)
    }
}
impl std::fmt::Display for Fallacy {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Fallacy::FormalFallacy(formal) => write!(f, "Formal: {}", formal),
            Fallacy::InformalFallacy(informal) => write!(f, "Informal: {}", informal),
        }
    }
}
impl Fallacy {
    pub(crate) fn get_filename(&self) -> String {
        match self {
            Fallacy::FormalFallacy(formal) => format!("formal/{}", formal.get_filename()),
            Fallacy::InformalFallacy(informal) => format!("informal/{}", informal.get_filename()),
        }
    }
    // pub(crate) fn random() -> Self {
    //     let mut rng = rng();
    //     let variants = [
    //         Fallacy::FormalFallacy(FormalFallacy::random()),
    //         Fallacy::InformalFallacy(InformalFallacy::random()),
    //     ];
    //     variants.choose(&mut rng).unwrap().clone()
    // }
}
/// Formal fallacies, which are errors in the structure of an argument.
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub(crate) enum FormalFallacy {
    PropositionalFallacy,
    ProbabilisticFallacy,
    SyllogisticFallacy,
    QuantificationalFallacy
}
impl Default for FormalFallacy {
    fn default() -> Self {
        FormalFallacy::PropositionalFallacy
    }
}
impl std::fmt::Display for FormalFallacy {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            FormalFallacy::PropositionalFallacy => write!(f, "Propositional Fallacy"),
            FormalFallacy::ProbabilisticFallacy => write!(f, "Probabilistic Fallacy"),
            FormalFallacy::SyllogisticFallacy => write!(f, "Syllogistic Fallacy"),
            FormalFallacy::QuantificationalFallacy => write!(f, "Quantificational Fallacy"),
        }
    }
}
impl FormalFallacy {
    pub(crate) fn get_filename(&self) -> String {
        match self {
            FormalFallacy::PropositionalFallacy => "propositional".to_string(),
            FormalFallacy::ProbabilisticFallacy => "probabilistic".to_string(),
            FormalFallacy::SyllogisticFallacy => "syllogistic".to_string(),
            FormalFallacy::QuantificationalFallacy => "quantificational".to_string(),
        }
    }
    // pub(crate) fn random() -> Self {
    //     let mut rng = rng();
    //     let variants = [
    //         FormalFallacy::PropositionalFallacy,
    //         FormalFallacy::ProbabilisticFallacy,
    //         FormalFallacy::SyllogisticFallacy,
    //         FormalFallacy::QuantificationalFallacy,
    //     ];
    //     variants.choose(&mut rng).unwrap().clone()
    // }
}
/// Informal fallacies, which are errors in reasoning that do not involve the structure of the argument.
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub(crate) enum InformalFallacy {
    PostHocErgoPropterHoc,
    SlipperySlope,
    TexasSharpshooter,
    HastyGeneralization,
    OverGeneralization,
    NoTrueScotsman,
    QuotingOutOfContext,
    AdHominem,
    TuQuoque,
    Bandwagon,
    StrawMan,
    AdIgnorantiam,
    SpecialPleading,
    BeggingTheQuestion
}
impl Default for InformalFallacy {
    fn default() -> Self {
        InformalFallacy::PostHocErgoPropterHoc
    }
}
impl std::fmt::Display for InformalFallacy {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            InformalFallacy::PostHocErgoPropterHoc => write!(f, "Post Hoc Ergo Propter Hoc"),
            InformalFallacy::SlipperySlope => write!(f, "Slippery Slope"),
            InformalFallacy::TexasSharpshooter => write!(f, "Texas Sharpshooter"),
            InformalFallacy::HastyGeneralization => write!(f, "Hasty Generalization"),
            InformalFallacy::OverGeneralization => write!(f, "Over Generalization"),
            InformalFallacy::NoTrueScotsman => write!(f, "No True Scotsman"),
            InformalFallacy::QuotingOutOfContext => write!(f, "Quoting Out Of Context"),
            InformalFallacy::AdHominem => write!(f, "Ad Hominem"),
            InformalFallacy::TuQuoque => write!(f, "Tu Quoque"),
            InformalFallacy::Bandwagon => write!(f, "Bandwagon"),
            InformalFallacy::StrawMan => write!(f, "Straw Man"),
            InformalFallacy::AdIgnorantiam => write!(f, "Ad Ignorantiam"),
            InformalFallacy::SpecialPleading => write!(f, "Special Pleading"),
            InformalFallacy::BeggingTheQuestion => write!(f, "Begging The Question"),
        }
    }
}
impl InformalFallacy {
    pub(crate) fn get_filename(&self) -> String {
        match self {
            InformalFallacy::PostHocErgoPropterHoc => "post_hoc_ergo_propter_hoc".to_string(),
            InformalFallacy::SlipperySlope => "slippery_slope".to_string(),
            InformalFallacy::TexasSharpshooter => "texas_sharpshooter".to_string(),
            InformalFallacy::HastyGeneralization => "hasty_generalization".to_string(),
            InformalFallacy::OverGeneralization => "over_generalization".to_string(),
            InformalFallacy::NoTrueScotsman => "no_true_scotsman".to_string(),
            InformalFallacy::QuotingOutOfContext => "quoting_out_of_context".to_string(),
            InformalFallacy::AdHominem => "ad_hominem".to_string(),
            InformalFallacy::TuQuoque => "tu_quoque".to_string(),
            InformalFallacy::Bandwagon => "bandwagon".to_string(),
            InformalFallacy::StrawMan => "straw_man".to_string(),
            InformalFallacy::AdIgnorantiam => "ad_ignorantiam".to_string(),
            InformalFallacy::SpecialPleading => "special_pleading".to_string(),
            InformalFallacy::BeggingTheQuestion => "begging_the_question".to_string(),
        }
    }
    // pub(crate) fn random() -> Self {
    //     let mut rng = rng();
    //     let variants = [
    //         InformalFallacy::PostHocErgoPropterHoc,
    //         InformalFallacy::SlipperySlope,
    //         InformalFallacy::TexasSharpshooter,
    //         InformalFallacy::HastyGeneralization,
    //         InformalFallacy::OverGeneralization,
    //         InformalFallacy::NoTrueScotsman,
    //         InformalFallacy::QuotingOutOfContext,
    //         InformalFallacy::AdHominem,
    //         InformalFallacy::TuQuoque,
    //         InformalFallacy::Bandwagon,
    //         InformalFallacy::StrawMan,
    //         InformalFallacy::AdIgnorantiam,
    //         InformalFallacy::SpecialPleading,
    //         InformalFallacy::BeggingTheQuestion,
    //     ];
    //     variants.choose(&mut rng).unwrap().clone()
    // }
}

/// Bias module, which includes various lessons related to cognitive biases and their impact on reasoning.
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub(crate) enum Bias {
    ConfirmationBias,
    TheHaloEffect,
    FundamentalAttributionError,
    InGroupBias,
    DunningKrugerEffect,
    BarnumEffect
}
impl Default for Bias {
    fn default() -> Self {
        Bias::ConfirmationBias
    }
}
impl std::fmt::Display for Bias {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Bias::ConfirmationBias => write!(f, "Confirmation Bias"),
            Bias::TheHaloEffect => write!(f, "The Halo Effect"),
            Bias::FundamentalAttributionError => write!(f, "Fundamental Attribution Error"),
            Bias::InGroupBias => write!(f, "In Group Bias"),
            Bias::DunningKrugerEffect => write!(f, "Dunning Kruger Effect"),
            Bias::BarnumEffect => write!(f, "Barnum Effect"),
        }
    }
}
impl Bias {
    pub(crate) fn get_filename(&self) -> String {
        match self {
            Bias::ConfirmationBias => "confirmation_bias".to_string(),
            Bias::TheHaloEffect => "the_halo_effect".to_string(),
            Bias::FundamentalAttributionError => "fundamental_attribution_error".to_string(),
            Bias::InGroupBias => "in_group_bias".to_string(),
            Bias::DunningKrugerEffect => "dunning_kruger_effect".to_string(),
            Bias::BarnumEffect => "barnum_effect".to_string(),
        }
    }
    // pub(crate) fn random() -> Self {
    //     let mut rng = rng();
    //     let variants = [
    //         Bias::ConfirmationBias,
    //         Bias::TheHaloEffect,
    //         Bias::FundamentalAttributionError,
    //         Bias::InGroupBias,
    //         Bias::DunningKrugerEffect,
    //         Bias::BarnumEffect,
    //     ];
    //     variants.choose(&mut rng).unwrap().clone()
    // }
}
/// Appraisal module, which includes various lessons related to the evaluation and appraisal of arguments.
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub(crate) enum Appraisal {
    ConversionToPropositional, 
    CounterArgument,
}
impl Default for Appraisal {
    fn default() -> Self {
        Appraisal::ConversionToPropositional
    }
}
impl std::fmt::Display for Appraisal {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Appraisal::ConversionToPropositional => write!(f, "Conversion to Propositional"),
            Appraisal::CounterArgument => write!(f, "Counter Argument"),
        }
    }
}
impl Appraisal {
    pub(crate) fn get_filename(&self) -> String {
        match self {
            Appraisal::ConversionToPropositional => "conversion_to_propositional".to_string(),
            Appraisal::CounterArgument => "counter_argument".to_string(),
        }
    }
    // pub(crate) fn random() -> Self {
    //     let mut rng = rng();
    //     let variants = [
    //         Appraisal::ConversionToPropositional,
    //         Appraisal::CounterArgument,
    //     ];
    //     variants.choose(&mut rng).unwrap().clone()
    // }
}
