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

use derive_more::Debug;
use rspack_error::Diagnostic;
use rspack_util::fx_hash::FxDashMap;

use crate::{
  ApplyContext, BoxedParserAndGeneratorBuilder, CompilationHooks, CompilerHooks, CompilerOptions,
  ConcatenatedModuleHooks, ContextModuleFactoryHooks, ModuleType, NormalModuleFactoryHooks,
  NormalModuleHooks, Plugin, PluginContext, ResolverFactory,
};

#[derive(Debug)]
pub struct PluginDriver {
  pub(crate) options: Arc<CompilerOptions>,
  pub plugins: Vec<Box<dyn Plugin>>,
  pub resolver_factory: Arc<ResolverFactory>,
  #[debug(skip)]
  pub registered_parser_and_generator_builder:
    FxDashMap<ModuleType, BoxedParserAndGeneratorBuilder>,
  /// Collecting error generated by plugin phase, e.g., `Syntax Error`
  pub diagnostics: Arc<Mutex<Vec<Diagnostic>>>,
  pub compiler_hooks: CompilerHooks,
  pub compilation_hooks: CompilationHooks,
  pub normal_module_factory_hooks: NormalModuleFactoryHooks,
  pub context_module_factory_hooks: ContextModuleFactoryHooks,
  pub normal_module_hooks: NormalModuleHooks,
  pub concatenated_module_hooks: ConcatenatedModuleHooks,
}

impl PluginDriver {
  pub fn new(
    options: Arc<CompilerOptions>,
    plugins: Vec<Box<dyn Plugin>>,
    resolver_factory: Arc<ResolverFactory>,
  ) -> Arc<Self> {
    let mut compiler_hooks = Default::default();
    let mut compilation_hooks = Default::default();
    let mut normal_module_factory_hooks = Default::default();
    let mut context_module_factory_hooks = Default::default();
    let mut normal_module_hooks = Default::default();
    let mut concatenated_module_hooks = Default::default();
    let mut registered_parser_and_generator_builder = FxDashMap::default();
    let mut apply_context = ApplyContext {
      registered_parser_and_generator_builder: &mut registered_parser_and_generator_builder,
      compiler_hooks: &mut compiler_hooks,
      compilation_hooks: &mut compilation_hooks,
      normal_module_factory_hooks: &mut normal_module_factory_hooks,
      context_module_factory_hooks: &mut context_module_factory_hooks,
      normal_module_hooks: &mut normal_module_hooks,
      concatenated_module_hooks: &mut concatenated_module_hooks,
    };
    for plugin in &plugins {
      plugin
        .apply(PluginContext::with_context(&mut apply_context), &options)
        .expect("TODO:");
    }

    Arc::new(Self {
      options: options.clone(),
      plugins,
      resolver_factory,
      registered_parser_and_generator_builder,
      diagnostics: Arc::new(Mutex::new(vec![])),
      compiler_hooks,
      compilation_hooks,
      normal_module_factory_hooks,
      context_module_factory_hooks,
      normal_module_hooks,
      concatenated_module_hooks,
    })
  }

  pub fn take_diagnostic(&self) -> Vec<Diagnostic> {
    let mut diagnostic = self.diagnostics.lock().expect("TODO:");
    std::mem::take(&mut diagnostic)
  }

  pub fn clear_cache(&self) {
    self.resolver_factory.clear_cache();
    for plugin in &self.plugins {
      plugin.clear_cache();
    }
  }
}
