use crate::prelude::*;
use di::ServiceProvider;
use miette::Report;

/// Application host, responsible for executing the application
///
/// [`HostBuilder`] takes care of building the [Host] and loading the
/// dependency injection [`ServiceProvider`].
pub struct Host {
    /// Dependency injection service provider
    pub services: ServiceProvider,
}

impl Host {
    /// Create a new [`Host`] from a configured [`ServiceProvider`].
    #[must_use]
    pub fn new(services: ServiceProvider) -> Self {
        Host { services }
    }

    /// Execute the application
    ///
    /// 1. Configure logging
    /// 2. Determine the command to execute
    /// 3. Execute the command
    pub async fn execute(&self) -> Result<bool, Report> {
        let _ = self.services.get_required::<Logger>();
        let shutdown = self.services.get_required::<Shutdown>();
        shutdown.listen();
        trace!("{} {}", APP_NAME, app_version_or_describe());
        let args = self.services.get_required::<ArgumentsProvider>();
        match args.get_command() {
            Command::Batch => self
                .services
                .get_required::<BatchCommand>()
                .execute_cli()
                .await
                .map_err(Report::new),
            Command::Config => self
                .services
                .get_required::<ConfigCommand>()
                .execute()
                .map_err(Report::new),
            Command::Cross => self
                .services
                .get_required::<CrossCommand>()
                .execute_cli()
                .await
                .map_err(Report::new),
            Command::Docs => Ok(self.services.get_required::<DocsCommand>().execute()),
            Command::Inspect => self
                .services
                .get_required::<InspectCommand>()
                .execute_cli()
                .map_err(Report::new),
            Command::Queue(QueueCommand::Add) => self
                .services
                .get_required::<QueueAddCommand>()
                .execute_cli()
                .await
                .map_err(Report::new),
            Command::Queue(QueueCommand::Fetch) => self
                .services
                .get_required::<QueueFetchCommand>()
                .execute_cli()
                .await
                .map_err(Report::new),
            Command::Queue(QueueCommand::List) => self
                .services
                .get_required::<QueueListCommand>()
                .execute_cli()
                .await
                .map_err(Report::new),
            Command::Queue(QueueCommand::Remove) => self
                .services
                .get_required::<QueueRemoveCommand>()
                .execute_cli()
                .await
                .map_err(Report::new),
            Command::Queue(QueueCommand::Summary) => self
                .services
                .get_required::<QueueSummaryCommand>()
                .execute_cli()
                .await
                .map_err(Report::new),
            Command::Spectrogram => self
                .services
                .get_required::<SpectrogramCommand>()
                .execute_cli()
                .await
                .map_err(Report::new),
            Command::Transcode => self
                .services
                .get_required::<TranscodeCommand>()
                .execute_cli()
                .await
                .map_err(Report::new),
            Command::Upload => self
                .services
                .get_required::<UploadCommand>()
                .execute_cli()
                .await
                .map_err(Report::new),
            Command::Verify => self
                .services
                .get_required::<VerifyCommand>()
                .execute_cli()
                .await
                .map_err(Report::new),
            Command::Version => Ok(self
                .services
                .get_required::<VersionCommand>()
                .execute()
                .await),
        }
    }
}
