use std::{
    fmt,
    ops::{Deref, Range},
};

use eyre::{bail, eyre};

#[derive(Clone, Eq, PartialEq)]
pub struct Span<'a> {
    complete_str: &'a str,
    range: Range<usize>,
}

impl<'a> Span<'a> {
    pub fn new(complete_str: &'a str) -> Self {
        Self {
            complete_str,
            range: 0..complete_str.len(),
        }
    }

    pub const fn empty() -> Self {
        Span {
            complete_str: "",
            range: 0..0,
        }
    }

    pub fn get(&self, slice: Range<usize>) -> Option<Self> {
        let start = self.range.start.checked_add(slice.start)?;
        let end = self.range.start.checked_add(slice.end)?;

        if end > self.range.end || end < start {
            return None;
        }

        Some(Self {
            complete_str: self.complete_str,
            range: Range { start, end },
        })
    }

    pub fn complete_str(&self) -> Self {
        Self::new(self.complete_str)
    }

    pub fn split_at(&self, i: usize) -> Option<(Self, Self)> {
        let head = self.get(0..i)?;
        let tail = self.get(i..self.range.len())?;
        Some((head, tail))
    }

    pub fn trim_end_matches(&self, p: &str) -> Self {
        if !self.ends_with(p) {
            return self.clone();
        }

        Self {
            range: self.range.start..self.range.end - p.len(),
            complete_str: self.complete_str,
        }
    }

    /// Try to merge the spans.
    ///
    /// If either spans is empty, this just returns the other one.
    /// This only works if spans are pointing into the same backing buffer, and are adjacent.
    pub fn try_merge(&self, other: &Self) -> eyre::Result<Self> {
        if self.is_empty() {
            return Ok(other.clone());
        }

        if other.is_empty() {
            return Ok(self.clone());
        }

        if self.complete_str.as_ptr() != other.complete_str.as_ptr() {
            bail!("Can't merge different strings");
        }

        if self.range.end == other.range.start {
            Ok(Self {
                range: self.range.start..other.range.end,
                ..*self
            })
        } else if self.range.start == other.range.end {
            Ok(Self {
                range: other.range.start..self.range.end,
                ..*self
            })
        } else {
            Err(eyre!("String: {:?}", self.complete_str)
                .wrap_err(eyre!("Span 2: {:?}", other.deref()))
                .wrap_err(eyre!("Span 1: {:?}", self.deref()))
                .wrap_err("Can't merge disjoint string spans"))
        }
    }
}

impl Deref for Span<'_> {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        &self.complete_str[self.range.clone()]
    }
}

impl<'a> fmt::Debug for Span<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("Span")
            .field(&self.range)
            .field(&self.deref())
            .finish()
    }
}
