use std::{
  fmt::Debug,
  sync::{Arc, Mutex},
};

use crate::{
  command::{CommandClause, CommandClauseType},
  errors::{PsdkFatherResult, PsdkLockError},
};

pub trait PsdkArgGenericType: Clone + Debug {}

pub trait PsdkArgBase<T: PsdkArgGenericType>: Clone + Debug {
  fn header(&self) -> T;
  fn clause(&self) -> PsdkFatherResult<CommandClause>;
}

pub trait PsdkArg<T: PsdkArgGenericType>: PsdkArgBase<T> {
  fn newline(&self) -> bool;
  fn prepend(&self, arg: &impl PsdkArg<T>) -> PsdkFatherResult<&Self>;
  fn append(&self, arg: &impl PsdkArg<T>) -> PsdkFatherResult<&Self>;
  fn clauses(&self) -> PsdkFatherResult<Vec<CommandClause>>;
  fn clear(&self) -> PsdkFatherResult<()>;
}

#[macro_export]
macro_rules! impl_psdk_arg_to_easy_arg {
  ($generic_type:ty, $impl_struct:ty, $newline:tt) => {
    impl psdk_father::args::PsdkArg<$generic_type> for $impl_struct {
      fn newline(&self) -> bool {
        $newline
      }

      fn prepend(
        &self,
        arg: &impl psdk_father::args::PsdkArg<$generic_type>,
      ) -> psdk_father::errors::PsdkFatherResult<&Self> {
        <Self as psdk_father::args::PsdkEasyArg<$generic_type>>::prepend(self, arg)
      }

      fn append(
        &self,
        arg: &impl psdk_father::args::PsdkArg<$generic_type>,
      ) -> psdk_father::errors::PsdkFatherResult<&Self> {
        <Self as psdk_father::args::PsdkEasyArg<$generic_type>>::append(self, arg)
      }

      fn clauses(
        &self,
      ) -> psdk_father::errors::PsdkFatherResult<Vec<psdk_father::command::CommandClause>> {
        <Self as psdk_father::args::PsdkEasyArg<$generic_type>>::clauses(self)
      }

      fn clear(&self) -> psdk_father::errors::PsdkFatherResult<()> {
        <Self as psdk_father::args::PsdkEasyArg<$generic_type>>::clear(self)
      }
    }
  };
}

impl PsdkArgGenericType for String {}
impl PsdkArgGenericType for u8 {}
impl PsdkArgGenericType for u16 {}
impl PsdkArgGenericType for u32 {}
impl PsdkArgGenericType for u128 {}
impl PsdkArgGenericType for usize {}
impl PsdkArgGenericType for i8 {}
impl PsdkArgGenericType for i16 {}
impl PsdkArgGenericType for i32 {}
impl PsdkArgGenericType for i128 {}
impl PsdkArgGenericType for isize {}
impl PsdkArgGenericType for Vec<String> {}
impl PsdkArgGenericType for Vec<u8> {}

#[derive(Clone, Debug, Default)]
pub struct ArgContext {
  prepend_clauses: Vec<CommandClause>,
  append_clauses: Vec<CommandClause>,
}

pub trait PsdkEasyArg<T: PsdkArgGenericType>: PsdkArg<T> {
  fn context(&self) -> Arc<Mutex<ArgContext>>;

  fn prepend(&self, arg: &impl PsdkArg<T>) -> PsdkFatherResult<&Self> {
    let ctx = self.context();
    let mut ctx = ctx
      .lock()
      .map_err(|e| PsdkLockError::LockArgContext(format!("{:?}", e)))?;
    let clauses = arg.clauses()?;
    ctx.prepend_clauses.extend(clauses);
    Ok(self)
  }

  fn append(&self, arg: &impl PsdkArg<T>) -> PsdkFatherResult<&Self> {
    let ctx = self.context();
    let mut ctx = ctx
      .lock()
      .map_err(|e| PsdkLockError::LockArgContext(format!("{:?}", e)))?;
    let clauses = arg.clauses()?;
    ctx.append_clauses.extend(clauses);
    Ok(self)
  }

  fn clauses(&self) -> PsdkFatherResult<Vec<CommandClause>> {
    let clause = self.clause()?;

    let ctx = self.context();
    let ctx = ctx
      .lock()
      .map_err(|e| PsdkLockError::LockArgContext(format!("{:?}", e)))?;
    let mut clauses = Vec::with_capacity(ctx.prepend_clauses.len() + ctx.append_clauses.len() + 1);
    clauses.extend(ctx.prepend_clauses.clone());

    if clause.type_ != CommandClauseType::None {
      clauses.push(clause);
      if self.newline() {
        clauses.push(CommandClause::newline());
      }
    }

    clauses.extend(ctx.append_clauses.clone());
    Ok(clauses)
  }

  fn clear(&self) -> PsdkFatherResult<()> {
    let ctx = self.context();
    let mut ctx = ctx
      .lock()
      .map_err(|e| PsdkLockError::LockArgContext(format!("{:?}", e)))?;
    ctx.prepend_clauses.clear();
    ctx.append_clauses.clear();
    Ok(())
  }
}
