use brisk_it::{
    generator::{Generator, Result},
    Either, Required,
};
use quote::quote;

//  ____                       _
// |  _ \ ___ _ __   ___  __ _| |_ ___ _ __
// | |_) / _ \ '_ \ / _ \/ _` | __/ _ \ '__|
// |  _ <  __/ |_) |  __/ (_| | ||  __/ |
// |_| \_\___| .__/ \___|\__,_|\__\___|_|
//           |_|

#[derive(Default)]
pub(super) struct Repeater {}

impl Generator for Repeater {
    fn generate(
        &self,
        input: brisk_it::component::ComponentInput,
        manager: &brisk_it::generator::Manager,
    ) -> Result<proc_macro2::TokenStream> {
        let mut content = Vec::<proc_macro2::TokenStream>::default();
        let mut target: Option<syn::Expr> = None;
        let mut model: Option<syn::Expr> = None;

        for prop in input.properties {
            if prop.name == "target" {
                target = Some(prop.expr)
            } else if prop.name == "model" {
                model = Some(prop.expr)
            } else {
                return Err(brisk_it::errors::unknown_property(prop));
            }
        }
        for child in input.children.into_iter() {
            content.push(manager.generate(child).either());
        }

        let target = target.required(&input.name, "target")?;
        let model = model.required(&input.name, "model")?;

        Ok(quote::quote! {
          for #target in #model {
            #(#content)*
          }
        })
    }
    fn boxed_clone(&self) -> Box<dyn Generator> {
        Box::new(Self {})
    }
}

//  __  __       _       _
// |  \/  | __ _| |_ ___| |__
// | |\/| |/ _` | __/ __| '_ \
// | |  | | (_| | || (__| | | |
// |_|  |_|\__,_|\__\___|_| |_|
//

#[derive(Default)]
pub(super) struct Match {}

impl Generator for Match {
    fn generate(
        &self,
        input: brisk_it::component::ComponentInput,
        manager: &brisk_it::generator::Manager,
    ) -> Result<proc_macro2::TokenStream> {
        let mut content = Vec::<proc_macro2::TokenStream>::default();
        let mut value: Option<syn::Expr> = None;

        for prop in input.properties {
            if prop.name == "value" {
                value = Some(prop.expr)
            } else {
                return Err(brisk_it::errors::unknown_property(prop));
            }
        }
        for matc in input.matches {
            let pat = matc.pattern;
            let arrow = matc.arrow_token;
            let component = manager.generate(matc.component).either();
            content.push(quote! {
                #pat #arrow { #component }
            });
        }

        let value = value.required(&input.name, "value")?;

        Ok(quote::quote! {
            match #value
            {
                #(#content)*
            }
        })
    }
    fn boxed_clone(&self) -> Box<dyn Generator> {
        Box::new(Self {})
    }
}
