use std::fmt::{Display, Formatter};
use crate::LogId;
use anyerror::AnyError;
use thiserror::Error;

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ErrorVerb {
    Read,
    Write,
    Seek,
    Delete,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ErrorSubject {
    OpenWriter,
    OpenReader,

    FlushWriter,
    AppendEntries { index: usize },
    TruncatePrefix { first_log_index_kept: usize },
    TruncateSuffix { last_log_index_kept: usize },
    ResetLogStorage { next_log_index: usize },
    GetLogEntry { log_index: usize },
    ReadLogId,
    WriteLogId { log_id: LogId },
    GenerateReaderId,
    DownloadSnapshot { reader_id: usize },
}

#[derive(Debug, Clone, Error, PartialEq, Eq)]
pub struct StorageError {
    subject: ErrorSubject,
    verb: ErrorVerb,
    source: AnyError,
    backtrace: Option<String>,
}

impl StorageError {
    pub fn new(subject: ErrorSubject, verb: ErrorVerb, source: impl Into<AnyError>) -> StorageError {
        Self {
            subject,
            verb,
            source: source.into(),
            backtrace: anyerror::backtrace_str(),
        }
    }

    pub fn open_reader(source: impl Into<AnyError>) -> Self {
        Self::new(ErrorSubject::OpenReader, ErrorVerb::Read, source)
    }

    pub fn get_log_entry(log_index: usize, source: impl Into<AnyError>) -> Self {
        Self::new(ErrorSubject::GetLogEntry { log_index }, ErrorVerb::Read, source)
    }

    pub fn open_writer(source: impl Into<AnyError>) -> Self {
        Self::new(ErrorSubject::OpenWriter, ErrorVerb::Write, source)
    }

    pub fn append_entries(index: usize, source: impl Into<AnyError>) -> Self {
        Self::new(ErrorSubject::AppendEntries { index }, ErrorVerb::Write, source)
    }

    pub fn flush_writer(source: impl Into<AnyError>) -> Self {
        Self::new(ErrorSubject::FlushWriter, ErrorVerb::Write, source)
    }

    pub fn truncate_prefix(first_log_index_kept: usize, source: impl Into<AnyError>) -> Self {
        Self::new(
            ErrorSubject::TruncatePrefix { first_log_index_kept },
            ErrorVerb::Write,
            source,
        )
    }

    pub fn truncate_suffix(last_log_index_kept: usize, source: impl Into<AnyError>) -> Self {
        Self::new(
            ErrorSubject::TruncateSuffix { last_log_index_kept },
            ErrorVerb::Write,
            source,
        )
    }

    pub fn reset(next_log_index: usize, source: impl Into<AnyError>) -> Self {
        Self::new(
            ErrorSubject::ResetLogStorage { next_log_index },
            ErrorVerb::Write,
            source,
        )
    }

    pub fn read_log_id(source: impl Into<AnyError>) -> Self {
        Self::new(ErrorSubject::ReadLogId, ErrorVerb::Read, source)
    }

    pub fn write_log_id(log_id: LogId, source: impl Into<AnyError>) -> Self {
        Self::new(ErrorSubject::WriteLogId { log_id }, ErrorVerb::Write, source)
    }

    pub fn download_snapshot(reader_id: usize, source: impl Into<AnyError>) -> Self {
        Self::new(ErrorSubject::DownloadSnapshot { reader_id }, ErrorVerb::Write, source)
    }

    pub fn generate_reader_id(source: impl Into<AnyError>) -> Self {
        Self::new(ErrorSubject::GenerateReaderId, ErrorVerb::Write, source)
    }
}


impl Display for StorageError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let _ = write!(f, " StorageError: \n");
        let _ = write!(f, "      Subject: {:?} \n", self.subject);
        let _=  write!(f, "         Verb: {:?} \n", self.verb);
        write!(f, "       Source: {}", self.source)
    }
}