use std::path::{Path, PathBuf};

use crate::builder::builder_options::BuilderOptions;
use crate::provider::{OptionsWatcher, WatcherOptions};

use super::OptionsRegistryBuilder;

/// A builder to use for local development to create an `OptionsWatcher` which changes the underlying `OptionsProvider` when files are changed.
///
/// This builder is kept separate from the `OptionsProviderBuilder` in order to keep `OptionsProviderBuilder` and `OptionsProvider` as simple and efficient as possible for production use.
#[derive(Clone)]
pub struct OptionsWatcherBuilder {
    builder_options: BuilderOptions,
    watched_directories: Vec<PathBuf>,
    watcher_options: WatcherOptions,
}

impl Default for OptionsWatcherBuilder {
    fn default() -> Self {
        Self::new()
    }
}

impl OptionsWatcherBuilder {
    pub fn new() -> Self {
        OptionsWatcherBuilder {
            builder_options: BuilderOptions::default(),
            watched_directories: Vec::new(),
            watcher_options: WatcherOptions::default(),
        }
    }

    pub fn with_watcher_options(&mut self, watcher_options: WatcherOptions) -> &mut Self {
        self.watcher_options = watcher_options;
        self
    }
}

impl OptionsRegistryBuilder<OptionsWatcher> for OptionsWatcherBuilder {
    fn add_directories(&mut self, directories: &[impl AsRef<Path>]) -> Result<&Self, String> {
        for directory in directories {
            self.watched_directories
                .push(directory.as_ref().to_path_buf());
        }
        Ok(self)
    }

    /// Add a directory to watch for changes.
    fn add_directory(&mut self, directory: impl AsRef<Path>) -> Result<&Self, String> {
        self.watched_directories
            .push(directory.as_ref().to_path_buf());
        Ok(self)
    }

    fn with_options(&mut self, options: BuilderOptions) -> Result<&Self, String> {
        self.builder_options = options;
        Ok(self)
    }

    fn with_schema(&mut self, schema_path: impl AsRef<Path>) -> Result<&Self, String> {
        self.builder_options.schema_path = Some(schema_path.as_ref().to_path_buf());
        Ok(self)
    }

    fn build(&mut self) -> Result<OptionsWatcher, String> {
        OptionsWatcher::new(
            &self.watched_directories,
            self.watcher_options.clone(),
            self.builder_options.clone(),
        )
    }
}
