#[derive(Debug, PartialEq, Clone)]
pub struct TokensVec(pub(crate) Vec<super::lexer::Token>);

impl From<Vec<super::lexer::Token>> for TokensVec
{
  fn from(value: Vec<super::lexer::Token>) -> Self
  {
    Self(value)
  }
}

#[derive(Debug, PartialEq, Clone)]
#[allow(missing_docs)]
#[non_exhaustive]
pub enum ErrorKind
{
  LexError,
  FloatingPointOverflow,
  IntegerOverflow,
  MalformedInteger,
  MalformedFloatingPoint,
  ExpectedTokens(TokensVec),
  UnexpectedToken,
  ConflictingEdgeDirections,
  PathAcceptSingleEdge,
  RequiresDirectedEdge,
  NoSingleRelationshipType,
  VariableNotFound,
}

pub(crate) fn parse_int_error_to_error_kind(e: std::num::ParseIntError) -> ErrorKind
{
  match e.kind()
  {
    std::num::IntErrorKind::Empty | std::num::IntErrorKind::InvalidDigit =>
    {
      ErrorKind::MalformedInteger
    }
    std::num::IntErrorKind::PosOverflow | std::num::IntErrorKind::NegOverflow =>
    {
      ErrorKind::IntegerOverflow
    }
    _ => unreachable!(),
  }
}

#[derive(Debug, PartialEq, Clone)]
pub struct Error
{
  pub(crate) kind: ErrorKind,
  pub(crate) span: std::ops::Range<usize>,
}

impl Error
{
  pub fn span(&self) -> &std::ops::Range<usize>
  {
    &self.span
  }
  pub fn error_kind(&self) -> &ErrorKind
  {
    &self.kind
  }
  pub fn nice_error_string(&self, source: &str) -> String
  {
    let error_message = format!("{}", self);
    let error_span = self.span();
    let error_snippet = &source[error_span.clone()];
    format!(
      "Error at {}..{}: '{error_snippet}': {error_message}",
      error_span.start, error_span.end
    )
  }
  pub(crate) fn new(kind: ErrorKind, span: std::ops::Range<usize>) -> Self
  {
    Self { kind, span }
  }
  pub(crate) fn from_parse_int_error(
    e: std::num::ParseIntError,
    span: std::ops::Range<usize>,
  ) -> Self
  {
    match e.kind()
    {
      std::num::IntErrorKind::Empty | std::num::IntErrorKind::InvalidDigit =>
      {
        Self::new(ErrorKind::MalformedInteger, span)
      }
      std::num::IntErrorKind::PosOverflow | std::num::IntErrorKind::NegOverflow =>
      {
        Self::new(ErrorKind::IntegerOverflow, span)
      }
      _ => unreachable!(),
    }
  }
}

ccutils::assert_impl_all!(Error: std::error::Error);

impl std::error::Error for Error {}

impl std::fmt::Display for Error
{
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
  {
    match &self.kind
    {
      ErrorKind::LexError => write!(f, "LexError at {}..{}.", self.span.start, self.span.end),
      ErrorKind::FloatingPointOverflow => write!(
        f,
        "FloatingPointOverflow: is too large to fit in a floating-point number at {}..{}.",
        self.span.start, self.span.end
      ),
      ErrorKind::IntegerOverflow => write!(
        f,
        "IntegerOverflow: is too large to fit in an integer at {}..{}.",
        self.span.start, self.span.end
      ),
      ErrorKind::MalformedInteger => write!(
        f,
        "MalformedInteger: is not a valid integer at {}..{}.",
        self.span.start, self.span.end
      ),
      ErrorKind::MalformedFloatingPoint => write!(
        f,
        "MalformedFloatingPoint: is not a valid floating-point number at {}..{}.",
        self.span.start, self.span.end
      ),
      ErrorKind::ExpectedTokens(expected) => write!(
        f,
        "ExpectedTokens: expected one of {:?} at {}..{}.",
        expected.0, self.span.start, self.span.end
      ),
      ErrorKind::UnexpectedToken => write!(
        f,
        "UnexpectedToken: unexpected token at {}..{}.",
        self.span.start, self.span.end
      ),
      ErrorKind::ConflictingEdgeDirections => write!(
        f,
        "ConflictingEdgeDirections: conflicting edge directions at {}..{}.",
        self.span.start, self.span.end
      ),
      ErrorKind::PathAcceptSingleEdge => write!(
        f,
        "PathAcceptSingleEdge: path patterns must consist of a single edge at {}..{}.",
        self.span.start, self.span.end
      ),
      ErrorKind::RequiresDirectedEdge => write!(
        f,
        "RequiresDirectedEdge: path patterns must consist of directed edges at {}..{}.",
        self.span.start, self.span.end
      ),
      ErrorKind::NoSingleRelationshipType => write!(
        f,
        "NoSingleRelationshipType: edge patterns must have exactly one relationship type at {}..{}.",
        self.span.start, self.span.end
      ),
      ErrorKind::VariableNotFound => write!(
        f,
        "VariableNotFound: variable in SEARCH clause not found in MATCH pattern at {}..{}.",
        self.span.start, self.span.end
      ),
    }
  }
}
