use core::panic;
use std::{borrow::Cow, fmt::Display, io, str};
use thiserror::Error;

use crate::id::{PersyId, RecRef};

pub(crate) type PERes<T> = Result<T, GenericError>;

/// Wrapper enum for all the possible Persy errors,
///
/// All the public functions use this enum as error return type
/// specialized with the specific error for the function.
///
/// to implement a catch all error for forward just implement a from
/// as follow
/// ```
/// enum YourError {
///     Persy(persy::PersyError),
/// }
///
/// impl<T: Into<persy::PersyError>> From<persy::PE<T>> for YourError {
///     fn from(err: persy::PE<T>) -> YourError {
///         YourError::Persy(err.error().into())
///    }
/// }
///
/// ```
///
#[derive(Debug)]
pub enum PE<T: Into<PersyError>> {
    PE(T),
}

impl<T: std::error::Error + Display + Into<PersyError>> std::error::Error for PE<T> {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            PE::PE(e) => e.source(),
        }
    }
}

impl<T: Display + Into<PersyError>> Display for PE<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            PE::PE(e) => e.fmt(f),
        }
    }
}

impl<T: Into<PersyError>> PE<T> {
    pub fn error(self) -> T {
        match self {
            PE::PE(e) => e,
        }
    }

    pub fn persy_error(self) -> PersyError {
        match self {
            PE::PE(e) => e.into(),
        }
    }
}

impl<T: Into<PersyError>> std::ops::Deref for PE<T> {
    type Target = T;
    fn deref(&self) -> &Self::Target {
        match self {
            PE::PE(e) => e,
        }
    }
}
impl<S, D> From<S> for PE<D>
where
    S: Into<D>,
    D: Into<PersyError>,
    S: Into<PersyError>,
{
    fn from(source: S) -> Self {
        PE::PE(source.into())
    }
}

/// Enum of all possible errors from Persy
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum PersyError {
    #[error("IO Error: {from} ")]
    Io { from: io::Error },
    #[error("String decoding error: {0}")]
    DecodingUtf8(str::Utf8Error),
    #[error("Failure acquiring lock for poisoning")]
    DecodingDataEncoding(data_encoding::DecodeError),
    #[error("Version Not Latest")]
    VersionNotLatest,
    #[error("Record Not Found {0}")]
    RecordNotFound(PersyId),
    #[error("Segment Not Found")]
    SegmentNotFound,
    #[error("Segment Already Exists")]
    SegmentAlreadyExists,
    #[error("Index Already Exists")]
    IndexAlreadyExists,
    #[error("Create and drop of a segment in the same transaction is not allowed")]
    CannotDropSegmentCreatedInTx,
    #[error("Create and drop of a index in the same transaction is not allowed")]
    CannotDropIndexCreatedInTx,
    #[error("Index Not Found")]
    IndexNotFound,
    #[error("Index method type mismatch persistent types: {0}")]
    IndexTypeMismatch(Cow<'static, str>),
    #[error("Found duplicate key:{0} for index: {1}")]
    IndexDuplicateKey(String, String),
    #[error("Reached the limit of retry changing the index")]
    ReachedLimitOfRetry,
    #[error("Timeout acquiring the data locks for the transaction")]
    TransactionTimeout,
    #[error("The id '{0}' has no valid format")]
    InvalidId(String),
    #[error("The id '{0}' has no valid format")]
    InvalidPersyId(RecRef),
    #[error("{0}")]
    InitError(String),
    #[error("Failure acquiring file lock: {0}")]
    AlreadyInUse(io::Error),
    #[error("File do not exists")]
    NotExists,
    #[error("Cannot create a new file already exists")]
    AlreadyExists,
    #[error("The file specified is not a Persy file")]
    NotPersyFile,
    #[error("Size of the record is too big")]
    RecordToBig,
    #[error("The key or the value are over the allowed size limit")]
    KeyOrValueTooBig,
    #[error("Varint Decoding error: {0}")]
    VarIntError(#[from] unsigned_varint::io::ReadError),
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum BeginTransactionError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("Transaction Id must be maximum 512 bytes")]
    InvalidTransactionId,
}

impl From<BeginTransactionError> for PersyError {
    fn from(e: BeginTransactionError) -> Self {
        match e {
            BeginTransactionError::Generic(e) => e.into(),
            BeginTransactionError::InvalidTransactionId => {
                PersyError::InvalidId("Transaction Id must be maximum 512 bytes".to_owned())
            }
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum CreateSegmentError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("Segment Already Exists")]
    SegmentAlreadyExists,
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum DropIndexError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("Index Not Found")]
    IndexNotFound,
    #[error("Create and drop of a index in the same transaction is not allowed")]
    CannotDropIndexCreatedInTx,
}

impl From<DropSegmentError> for DropIndexError {
    fn from(e: DropSegmentError) -> Self {
        match e {
            DropSegmentError::Generic(ee) => DropIndexError::Generic(ee),
            DropSegmentError::SegmentNotFound => DropIndexError::IndexNotFound,
            DropSegmentError::CannotDropSegmentCreatedInTx => DropIndexError::CannotDropIndexCreatedInTx,
        }
    }
}

impl From<DropIndexError> for PersyError {
    fn from(e: DropIndexError) -> Self {
        match e {
            DropIndexError::Generic(e) => e.into(),
            DropIndexError::IndexNotFound => PersyError::IndexNotFound,
            DropIndexError::CannotDropIndexCreatedInTx => PersyError::CannotDropIndexCreatedInTx,
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum DropSegmentError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("Segment Not Found")]
    SegmentNotFound,
    #[error("Create and drop of a segment in the same transaction is not allowed")]
    CannotDropSegmentCreatedInTx,
}

impl From<SegmentError> for DropSegmentError {
    fn from(e: SegmentError) -> Self {
        match e {
            SegmentError::Generic(ee) => DropSegmentError::Generic(ee),
            SegmentError::SegmentNotFound => DropSegmentError::SegmentNotFound,
        }
    }
}

impl From<DropSegmentError> for PersyError {
    fn from(e: DropSegmentError) -> Self {
        match e {
            DropSegmentError::Generic(e) => e.into(),
            DropSegmentError::SegmentNotFound => PersyError::SegmentNotFound,
            DropSegmentError::CannotDropSegmentCreatedInTx => PersyError::CannotDropSegmentCreatedInTx,
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum SegmentError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("Segment Not Found")]
    SegmentNotFound,
}

impl From<SegmentError> for PersyError {
    fn from(e: SegmentError) -> Self {
        match e {
            SegmentError::Generic(e) => e.into(),
            SegmentError::SegmentNotFound => PersyError::SegmentNotFound,
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ReadError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("Segment Not Found")]
    SegmentNotFound,
    #[error("The id '{0}' has no valid format")]
    InvalidPersyId(RecRef),
}

impl From<ReadError> for PersyError {
    fn from(e: ReadError) -> Self {
        match e {
            ReadError::Generic(e) => e.into(),
            ReadError::SegmentNotFound => PersyError::SegmentNotFound,
            ReadError::InvalidPersyId(e) => PersyError::InvalidPersyId(e),
        }
    }
}

impl From<SegmentError> for ReadError {
    fn from(e: SegmentError) -> Self {
        match e {
            SegmentError::Generic(e) => ReadError::Generic(e),
            SegmentError::SegmentNotFound => ReadError::SegmentNotFound,
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum CreateIndexError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("Index Already Exists")]
    IndexAlreadyExists,
}

impl From<CreateSegmentError> for CreateIndexError {
    fn from(e: CreateSegmentError) -> Self {
        match e {
            CreateSegmentError::Generic(ee) => CreateIndexError::Generic(ee),
            CreateSegmentError::SegmentAlreadyExists => CreateIndexError::IndexAlreadyExists,
        }
    }
}

impl From<InsertError> for CreateIndexError {
    fn from(e: InsertError) -> Self {
        match e {
            InsertError::Generic(ee) => CreateIndexError::Generic(ee),
            InsertError::SegmentNotFound => panic!("Segment should be created while creating index, impossible error"),
            InsertError::RecordToBig => panic!("Record size should never fail in index creation"),
        }
    }
}

impl From<CreateIndexError> for PersyError {
    fn from(e: CreateIndexError) -> Self {
        match e {
            CreateIndexError::Generic(er) => er.into(),
            CreateIndexError::IndexAlreadyExists => PersyError::IndexAlreadyExists,
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum IndexError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("Index Not Found")]
    IndexNotFound,
}

impl From<IndexError> for PersyError {
    fn from(e: IndexError) -> Self {
        match e {
            IndexError::Generic(e) => e.into(),
            IndexError::IndexNotFound => PersyError::IndexNotFound,
        }
    }
}

impl From<SegmentError> for IndexError {
    fn from(e: SegmentError) -> Self {
        match e {
            SegmentError::Generic(ex) => IndexError::Generic(ex),
            SegmentError::SegmentNotFound => IndexError::IndexNotFound,
        }
    }
}

impl From<ReadError> for IndexError {
    fn from(e: ReadError) -> Self {
        match e {
            ReadError::Generic(ex) => IndexError::Generic(ex),
            ReadError::SegmentNotFound => IndexError::IndexNotFound,
            ReadError::InvalidPersyId(_) => unreachable!(),
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum GenericError {
    #[error("IO Error: {from}")]
    Io { from: io::Error },
    #[error("String decoding error: {0}")]
    DecodingUtf8(#[from] str::Utf8Error),
    #[error("Varint Decoding error: {0}")]
    VarIntError(#[from] unsigned_varint::io::ReadError),
}
impl From<io::Error> for GenericError {
    fn from(err: io::Error) -> Self {
        #[cfg(feature = "test_backtraces")]
        eprintln!("{:?}", backtrace::Backtrace::new());
        GenericError::Io { from: err }
    }
}

impl From<io::Error> for PersyError {
    fn from(err: io::Error) -> Self {
        #[cfg(feature = "test_backtraces")]
        eprintln!("{:?}", backtrace::Backtrace::new());
        PersyError::Io { from: err }
    }
}

impl From<GenericError> for PersyError {
    fn from(e: GenericError) -> Self {
        match e {
            GenericError::Io { from } => PersyError::Io { from },
            GenericError::DecodingUtf8(e) => PersyError::DecodingUtf8(e),
            GenericError::VarIntError(e) => PersyError::VarIntError(e),
        }
    }
}

impl From<CreateSegmentError> for PersyError {
    fn from(e: CreateSegmentError) -> Self {
        match e {
            CreateSegmentError::Generic(e) => e.into(),
            CreateSegmentError::SegmentAlreadyExists => PersyError::SegmentAlreadyExists,
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum DeleteError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("The id '{0}' has no valid format")]
    RecordNotFound(PersyId),
    #[error("Segment Not Found")]
    SegmentNotFound,
    #[error("The id '{0}' has no valid format")]
    InvalidPersyId(RecRef),
}

impl From<ReadError> for DeleteError {
    fn from(e: ReadError) -> Self {
        match e {
            ReadError::Generic(e) => e.into(),
            ReadError::SegmentNotFound => DeleteError::SegmentNotFound,
            ReadError::InvalidPersyId(id) => DeleteError::InvalidPersyId(id),
        }
    }
}
impl From<SegmentError> for DeleteError {
    fn from(e: SegmentError) -> Self {
        match e {
            SegmentError::Generic(e) => e.into(),
            SegmentError::SegmentNotFound => DeleteError::SegmentNotFound,
        }
    }
}

impl From<DeleteError> for PersyError {
    fn from(e: DeleteError) -> Self {
        match e {
            DeleteError::Generic(e) => e.into(),
            DeleteError::RecordNotFound(id) => PersyError::RecordNotFound(id),
            DeleteError::SegmentNotFound => PersyError::SegmentNotFound,
            DeleteError::InvalidPersyId(id) => PersyError::InvalidPersyId(id),
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum UpdateError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("The id '{0}' has no valid format")]
    RecordNotFound(PersyId),
    #[error("Segment Not Found")]
    SegmentNotFound,
    #[error("Size of the record is too big")]
    RecordToBig,
    #[error("The id '{0}' has no valid format")]
    InvalidPersyId(RecRef),
}

impl From<SegmentError> for UpdateError {
    fn from(e: SegmentError) -> Self {
        match e {
            SegmentError::Generic(e) => e.into(),
            SegmentError::SegmentNotFound => UpdateError::SegmentNotFound,
        }
    }
}

impl From<ReadError> for UpdateError {
    fn from(e: ReadError) -> Self {
        match e {
            ReadError::Generic(e) => e.into(),
            ReadError::SegmentNotFound => UpdateError::SegmentNotFound,
            ReadError::InvalidPersyId(id) => UpdateError::InvalidPersyId(id),
        }
    }
}

impl From<UpdateError> for PersyError {
    fn from(e: UpdateError) -> Self {
        match e {
            UpdateError::Generic(e) => e.into(),
            UpdateError::RecordToBig => PersyError::RecordToBig,
            UpdateError::RecordNotFound(id) => PersyError::RecordNotFound(id),
            UpdateError::SegmentNotFound => PersyError::SegmentNotFound,
            UpdateError::InvalidPersyId(id) => PersyError::InvalidPersyId(id),
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum IndexOpsError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("Index Not Found")]
    IndexNotFound,
    #[error("Index method type mismatch persistent types: {0}")]
    IndexTypeMismatch(Cow<'static, str>),
}

impl From<IndexOpsError> for PersyError {
    fn from(e: IndexOpsError) -> Self {
        match e {
            IndexOpsError::Generic(e) => e.into(),
            IndexOpsError::IndexNotFound => PersyError::SegmentNotFound,
            IndexOpsError::IndexTypeMismatch(s) => PersyError::IndexTypeMismatch(s),
        }
    }
}

impl From<IndexError> for IndexOpsError {
    fn from(e: IndexError) -> Self {
        match e {
            IndexError::Generic(e) => IndexOpsError::Generic(e),
            IndexError::IndexNotFound => IndexOpsError::IndexNotFound,
        }
    }
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum PrepareError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("Index Not Found")]
    IndexNotFound,
    #[error("Segment Not Found")]
    SegmentNotFound,
    #[error("Segment Already Exists")]
    SegmentAlreadyExists,
    #[error("Index Already Exists")]
    IndexAlreadyExists,
    #[error("Timeout acquiring the data locks for the transaction")]
    TransactionTimeout,
    #[error("Record Not Found {0}")]
    RecordNotFound(PersyId),
    #[error("Version Not Latest")]
    VersionNotLatest,
    #[error("Reached the limit of retry changing the index")]
    ReachedLimitOfRetry,
    #[error("Found duplicate key:{0} for index: {1}")]
    IndexDuplicateKey(String, String),
}

impl From<ReadError> for PrepareError {
    fn from(read: ReadError) -> PrepareError {
        match read {
            ReadError::Generic(e) => PrepareError::Generic(e),
            ReadError::SegmentNotFound => PrepareError::SegmentNotFound,
            ReadError::InvalidPersyId(_) => panic!("Invalid id should have failed before"),
        }
    }
}

impl From<TimeoutError> for PrepareError {
    fn from(e: TimeoutError) -> Self {
        match e {
            TimeoutError::LockTimeout => PrepareError::TransactionTimeout,
        }
    }
}

impl From<PrepareError> for PersyError {
    fn from(e: PrepareError) -> Self {
        match e {
            PrepareError::Generic(ee) => ee.into(),
            PrepareError::IndexNotFound => PersyError::IndexNotFound,
            PrepareError::SegmentNotFound => PersyError::SegmentNotFound,
            PrepareError::SegmentAlreadyExists => PersyError::SegmentAlreadyExists,
            PrepareError::IndexAlreadyExists => PersyError::IndexAlreadyExists,
            PrepareError::TransactionTimeout => PersyError::TransactionTimeout,
            PrepareError::RecordNotFound(id) => PersyError::RecordNotFound(id),
            PrepareError::VersionNotLatest => PersyError::VersionNotLatest,
            PrepareError::ReachedLimitOfRetry => PersyError::ReachedLimitOfRetry,
            PrepareError::IndexDuplicateKey(a, b) => PersyError::IndexDuplicateKey(a, b),
        }
    }
}

#[derive(Debug, Error)]
pub enum TimeoutError {
    #[error("Timeout acquiring the data locks")]
    LockTimeout,
}

impl From<TimeoutError> for PersyError {
    fn from(e: TimeoutError) -> Self {
        match e {
            TimeoutError::LockTimeout => PersyError::TransactionTimeout,
        }
    }
}

pub type PIRes<T> = Result<T, IndexChangeError>;

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum IndexChangeError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("Timeout acquiring the data locks")]
    LockTimeout,
    #[error("Reached the limit of retry changing the index")]
    ReachedLimitOfRetry,
    #[error("Index Not Found")]
    IndexNotFound,
    #[error("Index method type mismatch persistent types: {0}")]
    IndexTypeMismatch(Cow<'static, str>),
    #[error("Found duplicate key:{0} for index: {1}")]
    IndexDuplicateKey(String, String),
}

impl From<TimeoutError> for IndexChangeError {
    fn from(e: TimeoutError) -> Self {
        match e {
            TimeoutError::LockTimeout => IndexChangeError::LockTimeout,
        }
    }
}

impl From<IndexOpsError> for IndexChangeError {
    fn from(e: IndexOpsError) -> Self {
        match e {
            IndexOpsError::Generic(ee) => IndexChangeError::Generic(ee),
            IndexOpsError::IndexNotFound => IndexChangeError::IndexNotFound,
            IndexOpsError::IndexTypeMismatch(s) => IndexChangeError::IndexTypeMismatch(s),
        }
    }
}

impl From<IndexError> for IndexChangeError {
    fn from(e: IndexError) -> Self {
        match e {
            IndexError::Generic(ee) => IndexChangeError::Generic(ee),
            IndexError::IndexNotFound => IndexChangeError::IndexNotFound,
        }
    }
}

impl From<SegmentError> for IndexChangeError {
    fn from(e: SegmentError) -> Self {
        match e {
            SegmentError::Generic(ee) => IndexChangeError::Generic(ee),
            SegmentError::SegmentNotFound => IndexChangeError::IndexNotFound,
        }
    }
}

impl From<DeleteError> for IndexChangeError {
    fn from(e: DeleteError) -> Self {
        match e {
            DeleteError::Generic(ee) => IndexChangeError::Generic(ee),
            DeleteError::SegmentNotFound => IndexChangeError::IndexNotFound,
            DeleteError::RecordNotFound(_) => panic!("Record should be protected by lock while index update"),
            DeleteError::InvalidPersyId(_) => panic!("Internally should never get and invalid id"),
        }
    }
}

impl From<UpdateError> for IndexChangeError {
    fn from(e: UpdateError) -> Self {
        match e {
            UpdateError::Generic(ee) => IndexChangeError::Generic(ee),
            UpdateError::SegmentNotFound => IndexChangeError::IndexNotFound,
            UpdateError::RecordToBig => panic!("Record size should be limited by key sizes"),
            UpdateError::RecordNotFound(_) => panic!("Record should be protected by lock while index update"),
            UpdateError::InvalidPersyId(_) => panic!("Internally should never get an invalid id"),
        }
    }
}

impl From<InsertError> for IndexChangeError {
    fn from(e: InsertError) -> Self {
        match e {
            InsertError::Generic(ee) => IndexChangeError::Generic(ee),
            InsertError::SegmentNotFound => IndexChangeError::IndexNotFound,
            InsertError::RecordToBig => panic!("Record size should be limited by key sizes"),
        }
    }
}

impl From<IndexChangeError> for PrepareError {
    fn from(e: IndexChangeError) -> Self {
        match e {
            IndexChangeError::Generic(ee) => PrepareError::Generic(ee),
            IndexChangeError::IndexNotFound => PrepareError::IndexNotFound,
            IndexChangeError::IndexTypeMismatch(_) => {
                panic!("In the prepare context should not be there a index type miss match")
            }
            IndexChangeError::LockTimeout => PrepareError::TransactionTimeout,
            IndexChangeError::ReachedLimitOfRetry => PrepareError::ReachedLimitOfRetry,
            IndexChangeError::IndexDuplicateKey(a, b) => PrepareError::IndexDuplicateKey(a, b),
        }
    }
}

impl From<IndexChangeError> for PersyError {
    fn from(e: IndexChangeError) -> Self {
        match e {
            IndexChangeError::Generic(ee) => ee.into(),
            IndexChangeError::IndexNotFound => PersyError::IndexNotFound,
            IndexChangeError::IndexTypeMismatch(i) => PersyError::IndexTypeMismatch(i),
            IndexChangeError::LockTimeout => PersyError::TransactionTimeout,
            IndexChangeError::ReachedLimitOfRetry => PersyError::ReachedLimitOfRetry,
            IndexChangeError::IndexDuplicateKey(a, b) => PersyError::IndexDuplicateKey(a, b),
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum IndexPutError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("Index Not Found")]
    IndexNotFound,
    #[error("Index method type mismatch persistent types: {0}")]
    IndexTypeMismatch(Cow<'static, str>),
    #[error("The key or the value are over the allowed size limit")]
    KeyOrValueTooBig,
}
impl From<IndexOpsError> for IndexPutError {
    fn from(e: IndexOpsError) -> Self {
        match e {
            IndexOpsError::Generic(ee) => Self::Generic(ee),
            IndexOpsError::IndexNotFound => Self::IndexNotFound,
            IndexOpsError::IndexTypeMismatch(s) => Self::IndexTypeMismatch(s),
        }
    }
}

impl From<IndexPutError> for PersyError {
    fn from(e: IndexPutError) -> Self {
        match e {
            IndexPutError::Generic(ee) => ee.into(),
            IndexPutError::IndexNotFound => Self::IndexNotFound,
            IndexPutError::IndexTypeMismatch(i) => Self::IndexTypeMismatch(i),
            IndexPutError::KeyOrValueTooBig => Self::KeyOrValueTooBig,
        }
    }
}

impl From<IndexError> for IndexPutError {
    fn from(e: IndexError) -> Self {
        match e {
            IndexError::Generic(ee) => Self::Generic(ee),
            IndexError::IndexNotFound => Self::IndexNotFound,
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum InvalidPersyId {
    #[error("String decoding error: {0}")]
    DecodingUtf8(#[from] str::Utf8Error),
    #[error("Failure acquiring lock for poisoning")]
    DecodingDataEncoding(#[from] data_encoding::DecodeError),
    #[error("The id '{0}' has no valid format")]
    InvalidPersyId(String),
}

impl From<InvalidPersyId> for PersyError {
    fn from(e: InvalidPersyId) -> Self {
        match e {
            InvalidPersyId::InvalidPersyId(id) => PersyError::InvalidId(id),
            InvalidPersyId::DecodingUtf8(err) => PersyError::DecodingUtf8(err),
            InvalidPersyId::DecodingDataEncoding(err) => PersyError::DecodingDataEncoding(err),
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum OpenError {
    #[error("Failure acquiring file lock: {0}")]
    AlreadyInUse(io::Error),
    #[error("File do not exists")]
    NotExists,
    #[error("Cannot create a new file already exists")]
    AlreadyExists,
    #[error("The file specified is not a Persy file")]
    NotPersyFile,
    #[error("{0}")]
    InitError(String),
    #[error(transparent)]
    Generic(#[from] GenericError),
}

impl From<OpenError> for PersyError {
    fn from(e: OpenError) -> Self {
        match e {
            OpenError::AlreadyInUse(err) => PersyError::AlreadyInUse(err),
            OpenError::InitError(e) => PersyError::InitError(e),
            OpenError::NotExists => PersyError::NotExists,
            OpenError::AlreadyExists => PersyError::AlreadyExists,
            OpenError::NotPersyFile => PersyError::NotPersyFile,
            OpenError::Generic(ge) => ge.into(),
        }
    }
}
impl From<io::Error> for OpenError {
    fn from(err: io::Error) -> Self {
        if err.kind() == io::ErrorKind::NotFound {
            OpenError::NotExists
        } else if err.raw_os_error() == fs2::lock_contended_error().raw_os_error() {
            OpenError::AlreadyInUse(err)
        } else if err.kind() == io::ErrorKind::AlreadyExists {
            OpenError::AlreadyExists
        } else {
            OpenError::from(GenericError::from(err))
        }
    }
}

impl From<CreateError> for OpenError {
    fn from(e: CreateError) -> Self {
        match e {
            CreateError::AlreadyInUse(err) => OpenError::AlreadyInUse(err),
            CreateError::AlreadyExists => OpenError::AlreadyExists,
            CreateError::Generic(ge) => OpenError::Generic(ge),
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum OpenMemoryError {
    #[error("{0}")]
    InitError(String),
    #[error(transparent)]
    Generic(#[from] GenericError),
}

impl From<OpenMemoryError> for PersyError {
    fn from(e: OpenMemoryError) -> Self {
        match e {
            OpenMemoryError::InitError(e) => PersyError::InitError(e),
            OpenMemoryError::Generic(ge) => ge.into(),
        }
    }
}

impl From<OpenError> for OpenMemoryError {
    fn from(e: OpenError) -> Self {
        match e {
            OpenError::AlreadyInUse(_) => unreachable!(),
            OpenError::InitError(ie) => OpenMemoryError::InitError(ie),
            OpenError::NotExists => unreachable!(),
            OpenError::AlreadyExists => unreachable!(),
            OpenError::NotPersyFile => unreachable!(),
            OpenError::Generic(ge) => OpenMemoryError::Generic(ge),
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum CreateError {
    #[error("Failure acquiring file lock: {0}")]
    AlreadyInUse(io::Error),
    #[error("Cannot create a new file already exists")]
    AlreadyExists,
    #[error(transparent)]
    Generic(#[from] GenericError),
}

impl From<CreateError> for PersyError {
    fn from(e: CreateError) -> Self {
        match e {
            CreateError::AlreadyInUse(err) => PersyError::AlreadyInUse(err),
            CreateError::AlreadyExists => PersyError::AlreadyExists,
            CreateError::Generic(ge) => ge.into(),
        }
    }
}
impl From<io::Error> for CreateError {
    fn from(err: io::Error) -> Self {
        if err.raw_os_error() == fs2::lock_contended_error().raw_os_error() {
            CreateError::AlreadyInUse(err)
        } else if err.kind() == io::ErrorKind::AlreadyExists {
            CreateError::AlreadyExists
        } else {
            CreateError::from(GenericError::from(err))
        }
    }
}

impl From<OpenError> for CreateError {
    fn from(open: OpenError) -> Self {
        match open {
            OpenError::Generic(g) => g.into(),
            OpenError::NotExists => unreachable!(),
            OpenError::InitError(_) => unreachable!(),
            OpenError::AlreadyInUse(x) => CreateError::AlreadyInUse(x),
            OpenError::NotPersyFile => unreachable!(),
            OpenError::AlreadyExists => CreateError::AlreadyExists,
        }
    }
}

#[derive(Debug, Error)]
#[non_exhaustive]
pub enum InsertError {
    #[error(transparent)]
    Generic(#[from] GenericError),
    #[error("Size of the record is too big")]
    RecordToBig,
    #[error("Segment Not Found")]
    SegmentNotFound,
}

impl From<InsertError> for PersyError {
    fn from(e: InsertError) -> Self {
        match e {
            InsertError::RecordToBig => PersyError::RecordToBig,
            InsertError::Generic(ge) => ge.into(),
            InsertError::SegmentNotFound => PersyError::SegmentNotFound,
        }
    }
}

impl From<SegmentError> for InsertError {
    fn from(e: SegmentError) -> Self {
        match e {
            SegmentError::Generic(e) => InsertError::Generic(e),
            SegmentError::SegmentNotFound => InsertError::SegmentNotFound,
        }
    }
}
