use serde::Serialize;

use crate::{CleanupState, TaskSummary, custom::CustomProg, dispatch_progress, fetch::FetchProg, file::{FileProgCopy, FileProgDelete, FileProgDownload, FileProgHardlink, FileProgLink, FileProgMove, FileProgTrash, FileProgUpload}, impl_from_prog, plugin::PluginProgEntry, preload::PreloadProg, process::{ProcessProgBg, ProcessProgBlock, ProcessProgOrphan}, size::SizeProg};

pub trait Progress: Copy {
	// Whether the task is still cooking or cleaning.
	fn running(self) -> bool;

	// Whether the task succeeded, regardless cleanup.
	// For tasks without a cleanup, this is the same as `success()`.
	fn cooked(self) -> bool;

	// Whether the task fully succeeded, including cleanup if applicable.
	fn success(self) -> bool {
		match self.cleaned() {
			None | Some(CleanupState::Success) => self.cooked(),
			Some(CleanupState::Pending | CleanupState::Failed) => false,
		}
	}

	// Whether the task fully failed.
	//
	// For tasks with a collect phase, e.g. gathering files to copy:
	//   collect failed, or cleanup failed if applicable, regardless main work.
	// For tasks without a collect phase:
	//   main work failed, or cleanup failed if applicable.
	fn failed(self) -> bool;

	// Cleanup state if the task has a cleanup phase, otherwise `None`.
	fn cleaned(self) -> Option<CleanupState> { None }

	// Optional percentage for UI display.
	fn percent(self) -> Option<f32> { None }

	// Helper for tasks that are still cooking or cleaning.
	fn cooking_or_cleaning(self, cooking: bool) -> bool {
		cooking || (self.cooked() && self.cleaned() == Some(CleanupState::Pending))
	}

	// Helper for workload-based progress calculations.
	fn work_percent(self, processed: u64, workload: u64, total: u32) -> f32 {
		if self.success() {
			return 100.0;
		}

		let percent = if workload != 0 {
			processed as f32 / workload as f32 * 100.0
		} else if total != 0 {
			100.0
		} else {
			0.0
		};

		if self.running() { 99.99f32.min(percent) } else { 100.0f32.min(percent) }
	}
}

// --- TaskProg
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
#[serde(tag = "kind")]
pub enum TaskProg {
	// File
	FileCopy(FileProgCopy),
	FileMove(FileProgMove),
	FileLink(FileProgLink),
	FileHardlink(FileProgHardlink),
	FileDelete(FileProgDelete),
	FileTrash(FileProgTrash),
	FileDownload(FileProgDownload),
	FileUpload(FileProgUpload),
	// Plugin
	PluginEntry(PluginProgEntry),
	// Fetch
	Fetch(FetchProg),
	// Preload
	Preload(PreloadProg),
	// Size
	Size(SizeProg),
	// Process
	ProcessBlock(ProcessProgBlock),
	ProcessOrphan(ProcessProgOrphan),
	ProcessBg(ProcessProgBg),
	// Custom
	Custom(CustomProg),
}

impl_from_prog! {
	// File
	FileCopy(FileProgCopy), FileMove(FileProgMove), FileLink(FileProgLink), FileHardlink(FileProgHardlink), FileDelete(FileProgDelete), FileTrash(FileProgTrash), FileDownload(FileProgDownload), FileUpload(FileProgUpload),
	// Plugin
	PluginEntry(PluginProgEntry),
	// Fetch
	Fetch(FetchProg),
	// Preload
	Preload(PreloadProg),
	// Size
	Size(SizeProg),
	// Process
	ProcessBlock(ProcessProgBlock), ProcessOrphan(ProcessProgOrphan), ProcessBg(ProcessProgBg),
	// Custom
	Custom(CustomProg),
}

impl From<TaskProg> for TaskSummary {
	fn from(value: TaskProg) -> Self {
		match value {
			// File
			TaskProg::FileCopy(p) => p.into(),
			TaskProg::FileMove(p) => p.into(),
			TaskProg::FileLink(p) => p.into(),
			TaskProg::FileHardlink(p) => p.into(),
			TaskProg::FileDelete(p) => p.into(),
			TaskProg::FileTrash(p) => p.into(),
			TaskProg::FileDownload(p) => p.into(),
			TaskProg::FileUpload(p) => p.into(),
			// Plugin
			TaskProg::PluginEntry(p) => p.into(),
			// Prework
			TaskProg::Fetch(p) => p.into(),
			TaskProg::Preload(p) => p.into(),
			TaskProg::Size(p) => p.into(),
			// Process
			TaskProg::ProcessBlock(p) => p.into(),
			TaskProg::ProcessOrphan(p) => p.into(),
			TaskProg::ProcessBg(p) => p.into(),
			// Custom
			TaskProg::Custom(p) => p.into(),
		}
	}
}

impl Progress for TaskProg {
	fn running(self) -> bool { dispatch_progress!(self, running) }

	fn cooked(self) -> bool { dispatch_progress!(self, cooked) }

	fn success(self) -> bool { dispatch_progress!(self, success) }

	fn failed(self) -> bool { dispatch_progress!(self, failed) }

	fn cleaned(self) -> Option<CleanupState> { dispatch_progress!(self, cleaned) }

	fn percent(self) -> Option<f32> { dispatch_progress!(self, percent) }
}

impl TaskProg {
	pub(crate) fn is_user(self) -> bool {
		match self {
			// File
			Self::FileCopy(_) => true,
			Self::FileMove(_) => true,
			Self::FileLink(_) => true,
			Self::FileHardlink(_) => true,
			Self::FileDelete(_) => true,
			Self::FileTrash(_) => true,
			Self::FileDownload(_) => true,
			Self::FileUpload(_) => true,
			// Plugin
			Self::PluginEntry(_) => true,
			// Prework
			Self::Fetch(_) => false,
			Self::Preload(_) => false,
			Self::Size(_) => false,
			// Process
			Self::ProcessBlock(_) => true,
			Self::ProcessOrphan(_) => true,
			Self::ProcessBg(_) => true,
			// Custom
			Self::Custom(_) => true,
		}
	}
}
