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

use thiserror::Error;

/// Result alias used throughout the library.
pub type MtagResult<T> = Result<T, MtagError>;

/// Errors that can occur while scanning, planning, or executing an organization run.
#[derive(Debug, Error)]
pub enum MtagError {
    /// A source directory could not be read during recursive scanning.
    #[error("failed to scan music folder `{path}`: {source}")]
    ReadDirectory {
        /// Directory that could not be read.
        path: PathBuf,
        /// Underlying filesystem error.
        #[source]
        source: io::Error,
    },
    /// A file type could not be inspected.
    #[error("failed to inspect file type `{path}`: {source}")]
    InspectFileType {
        /// File whose type could not be inspected.
        path: PathBuf,
        /// Underlying filesystem or MIME-sniffing error.
        #[source]
        source: io::Error,
    },
    /// Audio metadata could not be read.
    #[error("failed to read metadata from `{path}`: {source}")]
    ReadMetadata {
        /// Audio file whose tags could not be read.
        path: PathBuf,
        /// Underlying Lofty error.
        #[source]
        source: lofty::LoftyError,
    },
    /// The audio file had no readable tag block.
    #[error("no readable tags found in `{path}`")]
    MissingTags {
        /// File that had no readable tags.
        path: PathBuf,
    },
    /// A source or destination path had no final file-name component.
    #[error("path `{path}` has no file name")]
    MissingFileName {
        /// Path missing a file name.
        path: PathBuf,
    },
    /// The destination file already exists and the selected conflict policy forbids overwriting.
    #[error("destination already exists `{path}`")]
    DestinationExists {
        /// Destination path that already exists.
        path: PathBuf,
    },
    /// A conflict strategy string could not be parsed.
    #[error("invalid conflict strategy `{value}`")]
    InvalidConflictStrategy {
        /// User-provided strategy value.
        value: String,
    },
    /// A template references an unsupported variable.
    #[error("invalid template variable `{variable}`")]
    InvalidTemplateVariable {
        /// Unknown template variable name without braces.
        variable: String,
    },
    /// A template contains an opening brace without a matching closing brace.
    #[error("unclosed template variable in `{template}`")]
    UnclosedTemplateVariable {
        /// Template segment that could not be parsed.
        template: String,
    },
    /// A filesystem operation failed while copying, moving, or preparing destination directories.
    #[error("failed to {operation} `{from}` to `{to}`: {source}")]
    FileOperation {
        /// Human-readable operation name.
        operation: &'static str,
        /// Source file involved in the operation.
        from: PathBuf,
        /// Destination file involved in the operation.
        to: PathBuf,
        /// Underlying filesystem error.
        #[source]
        source: io::Error,
    },
}
