use std::{
    path::{Path, PathBuf},
    sync::{
        Arc,
        atomic::AtomicBool,
        mpsc::{self, Receiver, Sender},
    },
};

use anyhow::{Context, Result};
use cleanr_agent::{PathContext, PathInsight, create_agent};
use cleanr_config::{AgentConfig, Config};
use cleanr_core::{CleanupPlan, ExecutionManifest, RestoreManifest};
use cleanr_fs::{ScanOptions, ScanProgress, ScanReport, scan_paths_with_progress_cancellable};
use cleanr_i18n::I18n;
use cleanr_plugin_api::discover_bundles;
use cleanr_rules::RuleRegistry;
use cleanr_tasks::{
    CleanupAuthorization, CleanupExecutor, SystemRestoreExecutor, execute_cleanup_plan,
    list_execution_manifests, list_restore_manifests, restore_execution_manifest,
    write_cleanup_plan,
};

pub(crate) enum TaskEvent {
    ScanProgress(ScanProgress),
    ScanFinished(std::result::Result<ScanReport, String>),
}

pub(crate) enum InsightEvent {
    Finished(std::result::Result<PathInsight, String>),
}

pub(crate) struct ScanEffect {
    pub receiver: Receiver<TaskEvent>,
    pub cancellation: Arc<AtomicBool>,
}

pub(crate) fn load_runtime(config: &Config) -> Result<(RuleRegistry, I18n)> {
    let discovery = discover_bundles(
        &config.plugins.dirs,
        &config.plugins.trusted,
        env!("CARGO_PKG_VERSION"),
    );
    Ok((
        RuleRegistry::load_with_discovery(config, &discovery)?,
        I18n::load_with_discovery(config, &discovery)?,
    ))
}

pub(crate) fn insight_channel() -> (Sender<InsightEvent>, Receiver<InsightEvent>) {
    mpsc::channel()
}

pub(crate) fn spawn_scan(roots: Vec<PathBuf>, options: ScanOptions) -> Result<ScanEffect> {
    let (sender, receiver) = mpsc::channel();
    let cancellation = Arc::new(AtomicBool::new(false));
    let worker_cancellation = Arc::clone(&cancellation);
    std::thread::Builder::new()
        .name("cleanr-scan".to_string())
        .spawn(move || {
            let result = scan_paths_with_progress_cancellable(
                &roots,
                &options,
                &worker_cancellation,
                |progress| {
                    let _ = sender.send(TaskEvent::ScanProgress(progress));
                },
            )
            .map_err(|error| error.to_string());
            let _ = sender.send(TaskEvent::ScanFinished(result));
        })
        .context("failed to spawn scan worker")?;
    Ok(ScanEffect {
        receiver,
        cancellation,
    })
}

pub(crate) fn spawn_insight(
    config: AgentConfig,
    path: PathBuf,
    context: PathContext,
    sender: Sender<InsightEvent>,
) -> Result<()> {
    std::thread::Builder::new()
        .name("cleanr-insight".to_string())
        .spawn(move || {
            let result = create_agent(&config)
                .and_then(|agent| agent.explain_path(&path, &context))
                .map_err(|error| error.to_string());
            let _ = sender.send(InsightEvent::Finished(result));
        })
        .context("failed to spawn insight worker")?;
    Ok(())
}

pub(crate) fn load_history(
    state_dir: &Path,
) -> Result<(Vec<ExecutionManifest>, Vec<RestoreManifest>)> {
    Ok((
        list_execution_manifests(state_dir)?,
        list_restore_manifests(state_dir)?,
    ))
}

pub(crate) fn execute_cleanup(
    plan: &CleanupPlan,
    executor: &impl CleanupExecutor,
    state_dir: &Path,
    user_authorized: bool,
) -> Result<ExecutionManifest> {
    let authorization = user_authorized.then(CleanupAuthorization::explicit_user_confirmation);
    execute_cleanup_plan(plan, executor, state_dir, authorization.as_ref())
}

pub(crate) fn restore_cleanup(
    manifest: &ExecutionManifest,
    state_dir: &Path,
) -> Result<RestoreManifest> {
    restore_execution_manifest(manifest, &SystemRestoreExecutor, state_dir)
}

pub(crate) fn export_cleanup_plan(plan: &CleanupPlan, path: &Path) -> Result<()> {
    write_cleanup_plan(plan, path)
}

pub(crate) fn save_config(config: &Config, path: &Path) -> Result<()> {
    config.save_to(path)
}
