use crate::{CalibrationSource, Item, Scope};
use indexmap::IndexSet;
use snafu::{Backtrace, Snafu};

#[derive(Debug, Snafu)]
#[snafu(visibility(pub))]
pub enum Error {
    #[snafu(display("serde json error: {:?}", source))]
    SerdeJson {
        source: serde_json::Error,
        backtrace: Backtrace,
    },

    #[snafu(display("serde cbor error: {:?}", source))]
    SerdeCbor {
        source: serde_cbor::error::Error,
        backtrace: Backtrace,
    },

    #[snafu(display("i/o error: {:?}", source))]
    Io {
        source: std::io::Error,
        backtrace: Backtrace,
    },

    #[snafu(display(
        "scope {:?}: invalid source name '{}' \
         in bin type '{}'",
        scope,
        name,
        bin_type
    ))]
    InvalidSourceName {
        scope: Scope,
        name: String,
        bin_type: String,
        backtrace: Backtrace,
    },

    #[snafu(display(
        "scope {:?}: missing source name '{}' \
         for bin type '{}'",
        scope,
        name,
        bin_type
    ))]
    MissingSourceName {
        scope: Scope,
        name: String,
        bin_type: String,
        backtrace: Backtrace,
    },

    #[snafu(display("invalid sink name: {:?}", name))]
    InvalidSinkName { name: String, backtrace: Backtrace },

    #[snafu(display(
        "scope {:?}: invalid set of sink names. \
         expected: {:?}, received: {:?}",
        scope,
        expected,
        received
    ))]
    InvalidSinkNames {
        scope: Scope,
        expected: IndexSet<String>,
        received: IndexSet<String>,
        backtrace: Backtrace,
    },

    #[snafu(display(
        "scope {:?}: invalid set of bin and pipe names. \
         bins: {:?}, pipes: {:?}",
        scope,
        bins,
        pipes
    ))]
    InvalidBinNamesInPipeline {
        scope: Scope,
        bins: IndexSet<String>,
        pipes: IndexSet<String>,
        backtrace: Backtrace,
    },

    #[snafu(display(
        "scope {:?}: bin with id {:?} not present",
        scope,
        bin
    ))]
    BinNotPresent {
        scope: Scope,
        bin: String,
        backtrace: Backtrace,
    },

    #[snafu(display("scope {:?}: circular pipe references", scope))]
    CircularPipeReference { scope: Scope, backtrace: Backtrace },

    #[snafu(display(
        "invalid jsonl format in file {}, line {}",
        path,
        line
    ))]
    InvalidJsonL {
        path: String,
        line: usize,
        backtrace: Backtrace,
    },

    #[snafu(display(
        "jsonl file {} line {} has invalid item count \
         (required: {}, found: {})",
        path,
        line,
        required,
        found
    ))]
    InvalidJsonLItemCount {
        path: String,
        line: usize,
        required: usize,
        found: usize,
        backtrace: Backtrace,
    },

    #[snafu(display("invalid directsource data"))]
    InvalidDirectSourceData { backtrace: Backtrace },

    #[snafu(display(
        "scope {:?}: invalid pipeline output {}",
        scope,
        index
    ))]
    InvalidPipelineOutput {
        scope: Scope,
        index: String,
        backtrace: Backtrace,
    },

    #[snafu(display(
        "scope {:?}, calibration {:?}: must be a strictly \
         inclining or strictly declining slope",
        scope,
        calibration
    ))]
    InvalidCalibrationCurve {
        scope: Scope,
        calibration: CalibrationSource,
        backtrace: Backtrace,
    },

    #[snafu(display("scope {:?}: invalid calibration", scope))]
    InvalidCalibration { scope: Scope, backtrace: Backtrace },

    #[snafu(display("scope {:?}: asynchronous end of data", scope))]
    AsynchronousEndOfData { scope: Scope, backtrace: Backtrace },

    #[snafu(display(
        "conversion error in {}: {:?} ({})",
        function,
        value,
        message
    ))]
    ConversionError {
        function: String,
        value: Item,
        message: String,
        backtrace: Backtrace,
    },

    #[snafu(display("scope {:?}: data provider gone", scope))]
    DataProviderGone { scope: Scope, backtrace: Backtrace },

    #[snafu(display("scope {:?}: no sources", scope))]
    NoSources { scope: Scope, backtrace: Backtrace },

    #[snafu(display("internal error"))]
    InternalError { backtrace: Backtrace },

    #[snafu(display("calculation of bins not properly executed"))]
    BinCalculation { backtrace: Backtrace },

    #[snafu(display("error getting calibration: {}", message))]
    GetCalibration {
        message: String,
        backtrace: Backtrace,
    },
}
