use std::{
    fmt::{self, Debug},
    hash::{Hash, Hasher},
    ops::{Index, IndexMut, Range},
};
use miette::{LabeledSpan, SourceOffset, SourceSpan};
#[cfg(feature = "serialize")]
use serde::{Serialize, Serializer as SerdeSerializer, ser::SerializeMap};
use oxc_allocator::{Allocator, CloneIn, Dummy};
use oxc_ast_macros::ast;
use oxc_estree::ESTree;
#[cfg(feature = "serialize")]
use oxc_estree::ESTreeSpan;
pub const SPAN: Span = Span::new(0, 0);
#[ast(visit)]
#[derive(Default, Clone, Copy, Eq, PartialOrd, Ord)]
#[generate_derive(ESTree)]
#[builder(skip)]
#[content_eq(skip)]
#[estree(
    no_type,
    flatten,
    no_ts_def,
    add_ts_def = "interface Span { start: number; end: number; range?: [number, number]; }"
)]
pub struct Span {
        pub start: u32,
            pub end: u32,
        #[estree(skip)]
    _align: PointerAlign,
}
impl Span {
                                #[inline]
    pub const fn new(start: u32, end: u32) -> Self {
        Self { start, end, _align: PointerAlign::new() }
    }
                                                pub fn empty(at: u32) -> Self {
        Self::new(at, at)
    }
                                            pub const fn sized(start: u32, size: u32) -> Self {
        Self::new(start, start + size)
    }
                                            pub const fn size(self) -> u32 {
        debug_assert!(self.start <= self.end);
        self.end - self.start
    }
                                            pub const fn is_empty(self) -> bool {
        debug_assert!(self.start <= self.end);
        self.start == self.end
    }
                                                #[inline]
    pub const fn is_unspanned(self) -> bool {
        self.const_eq(SPAN)
    }
                                                                                #[inline]
    pub const fn contains_inclusive(self, span: Span) -> bool {
        self.start <= span.start && span.end <= self.end
    }
                                                #[must_use]
    pub fn merge(self, other: Self) -> Self {
        Self::new(self.start.min(other.start), self.end.max(other.end))
    }
                                                                        #[must_use]
    pub fn expand(self, offset: u32) -> Self {
        Self::new(self.start.saturating_sub(offset), self.end.saturating_add(offset))
    }
                                                                            #[must_use]
    pub fn shrink(self, offset: u32) -> Self {
        let start = self.start.saturating_add(offset);
        let end = self.end.saturating_sub(offset);
        debug_assert!(start <= end, "Cannot shrink span past zero length");
        Self::new(start, end)
    }
                                                                                                #[must_use]
    pub const fn expand_left(self, offset: u32) -> Self {
        Self::new(self.start.saturating_sub(offset), self.end)
    }
                                                                                            #[must_use]
    pub const fn shrink_left(self, offset: u32) -> Self {
        let start = self.start.saturating_add(offset);
        debug_assert!(start <= self.end);
        Self::new(self.start.saturating_add(offset), self.end)
    }
                                                                                                #[must_use]
    pub const fn expand_right(self, offset: u32) -> Self {
        Self::new(self.start, self.end.saturating_add(offset))
    }
                                                                                        #[must_use]
    pub const fn shrink_right(self, offset: u32) -> Self {
        let end = self.end.saturating_sub(offset);
        debug_assert!(self.start <= end);
        Self::new(self.start, end)
    }
                                                                        #[must_use]
    pub const fn move_left(self, offset: u32) -> Self {
        let start = self.start.saturating_sub(offset);
        #[cfg(debug_assertions)]
        if start == 0 {
            debug_assert!(self.start == offset, "Cannot move span past zero length");
        }
        Self::new(start, self.end.saturating_sub(offset))
    }
                                                                        #[must_use]
    pub const fn move_right(self, offset: u32) -> Self {
        let end = self.end.saturating_add(offset);
        #[cfg(debug_assertions)]
        if end == u32::MAX {
            debug_assert!(
                u32::MAX.saturating_sub(offset) == self.end,
                "Cannot move span past `u32::MAX` length"
            );
        }
        Self::new(self.start.saturating_add(offset), end)
    }
                                                pub fn source_text(self, source_text: &str) -> &str {
        &source_text[self.start as usize..self.end as usize]
    }
                #[must_use]
    pub fn label<S: Into<String>>(self, label: S) -> LabeledSpan {
        LabeledSpan::new_with_span(Some(label.into()), self)
    }
        #[must_use]
    pub fn primary_label<S: Into<String>>(self, label: S) -> LabeledSpan {
        LabeledSpan::new_primary_with_span(Some(label.into()), self)
    }
        #[must_use]
    pub fn primary(self) -> LabeledSpan {
        LabeledSpan::new_primary_with_span(None, self)
    }
                                        #[expect(clippy::inline_always)]     #[inline(always)]
    const fn as_u64(self) -> u64 {
        if cfg!(target_endian = "little") {
            ((self.end as u64) << 32) | (self.start as u64)
        } else {
            ((self.start as u64) << 32) | (self.end as u64)
        }
    }
                        #[expect(clippy::inline_always)]
    #[inline(always)]
    const fn const_eq(self, other: Self) -> bool {
        if cfg!(target_pointer_width = "64") {
            self.as_u64() == other.as_u64()
        } else {
            self.start == other.start && self.end == other.end
        }
    }
}
impl Index<Span> for str {
    type Output = str;
    #[inline]
    fn index(&self, index: Span) -> &Self::Output {
        &self[index.start as usize..index.end as usize]
    }
}
impl IndexMut<Span> for str {
    #[inline]
    fn index_mut(&mut self, index: Span) -> &mut Self::Output {
        &mut self[index.start as usize..index.end as usize]
    }
}
impl From<Range<u32>> for Span {
    #[inline]
    fn from(range: Range<u32>) -> Self {
        Self::new(range.start, range.end)
    }
}
impl From<Span> for SourceSpan {
    fn from(val: Span) -> Self {
        Self::new(SourceOffset::from(val.start as usize), val.size() as usize)
    }
}
impl From<Span> for LabeledSpan {
    fn from(val: Span) -> Self {
        LabeledSpan::underline(val)
    }
}
impl PartialEq for Span {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        self.const_eq(*other)
    }
}
impl Hash for Span {
    #[inline]     fn hash<H: Hasher>(&self, hasher: &mut H) {
        if cfg!(target_pointer_width = "64") {
            self.as_u64().hash(hasher);
        } else {
            self.start.hash(hasher);
            self.end.hash(hasher);
        }
    }
}
#[expect(clippy::missing_fields_in_debug)]
impl Debug for Span {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Span").field("start", &self.start).field("end", &self.end).finish()
    }
}
pub trait GetSpan {
        fn span(&self) -> Span;
}
pub trait GetSpanMut {
        fn span_mut(&mut self) -> &mut Span;
}
impl GetSpan for Span {
    #[inline]
    fn span(&self) -> Span {
        *self
    }
}
impl GetSpanMut for Span {
    #[inline]
    fn span_mut(&mut self) -> &mut Span {
        self
    }
}
impl<'a> CloneIn<'a> for Span {
    type Cloned = Self;
    #[inline]
    fn clone_in(&self, _: &'a Allocator) -> Self {
        *self
    }
}
impl<'a> Dummy<'a> for Span {
        #[expect(clippy::inline_always)]
    #[inline(always)]
    fn dummy(_allocator: &'a Allocator) -> Self {
        SPAN
    }
}
#[cfg(feature = "serialize")]
impl Serialize for Span {
    fn serialize<S: SerdeSerializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry("start", &self.start)?;
        map.serialize_entry("end", &self.end)?;
        map.end()
    }
}
#[cfg(feature = "serialize")]
impl ESTreeSpan for Span {
    #[expect(clippy::inline_always)]     #[inline(always)]
    fn range(self) -> [u32; 2] {
        [self.start, self.end]
    }
}
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
struct PointerAlign([usize; 0]);
impl PointerAlign {
    #[inline]
    const fn new() -> Self {
        Self([])
    }
}
#[cfg(test)]
mod test {
    use super::Span;
    #[test]
    fn test_size() {
        let s = Span::sized(0, 5);
        assert_eq!(s.size(), 5);
        assert!(!s.is_empty());
        let s = Span::sized(5, 0);
        assert_eq!(s.size(), 0);
        assert!(s.is_empty());
    }
    #[test]
    fn test_hash() {
        use std::hash::{DefaultHasher, Hash, Hasher};
        fn hash<T: Hash>(value: T) -> u64 {
            let mut hasher = DefaultHasher::new();
            value.hash(&mut hasher);
            hasher.finish()
        }
        let first_hash = hash(Span::new(1, 5));
        let second_hash = hash(Span::new(1, 5));
        assert_eq!(first_hash, second_hash);
                #[cfg(target_pointer_width = "64")]
        {
            let u64_equivalent: u64 =
                if cfg!(target_endian = "little") { 1 + (5 << 32) } else { (1 << 32) + 5 };
            let u64_hash = hash(u64_equivalent);
            assert_eq!(first_hash, u64_hash);
        }
                #[cfg(not(target_pointer_width = "64"))]
        {
            #[derive(Hash)]
            #[repr(C)]
            struct PlainSpan {
                start: u32,
                end: u32,
            }
            let plain_hash = hash(PlainSpan { start: 1, end: 5 });
            assert_eq!(first_hash, plain_hash);
        }
    }
    #[test]
    fn test_eq() {
        assert_eq!(Span::new(0, 0), Span::new(0, 0));
        assert_eq!(Span::new(0, 1), Span::new(0, 1));
        assert_eq!(Span::new(1, 5), Span::new(1, 5));
        assert_ne!(Span::new(0, 0), Span::new(0, 1));
        assert_ne!(Span::new(1, 5), Span::new(0, 5));
        assert_ne!(Span::new(1, 5), Span::new(2, 5));
        assert_ne!(Span::new(1, 5), Span::new(1, 4));
        assert_ne!(Span::new(1, 5), Span::new(1, 6));
    }
    #[test]
    fn test_ordering_less() {
        assert!(Span::new(0, 0) < Span::new(0, 1));
        assert!(Span::new(0, 3) < Span::new(2, 5));
    }
    #[test]
    fn test_ordering_greater() {
        assert!(Span::new(0, 1) > Span::new(0, 0));
        assert!(Span::new(2, 5) > Span::new(0, 3));
    }
    #[test]
    fn test_contains() {
        let span = Span::new(5, 10);
        assert!(span.contains_inclusive(span));
        assert!(span.contains_inclusive(Span::new(5, 5)));
        assert!(span.contains_inclusive(Span::new(10, 10)));
        assert!(span.contains_inclusive(Span::new(6, 9)));
        assert!(!span.contains_inclusive(Span::new(0, 0)));
        assert!(!span.contains_inclusive(Span::new(4, 10)));
        assert!(!span.contains_inclusive(Span::new(5, 11)));
        assert!(!span.contains_inclusive(Span::new(4, 11)));
    }
    #[test]
    fn test_expand() {
        let span = Span::new(3, 5);
        assert_eq!(span.expand(0), Span::new(3, 5));
        assert_eq!(span.expand(1), Span::new(2, 6));
                assert_eq!(span.expand(5), Span::new(0, 10));
    }
    #[test]
    fn test_shrink() {
        let span = Span::new(4, 8);
        assert_eq!(span.shrink(0), Span::new(4, 8));
        assert_eq!(span.shrink(1), Span::new(5, 7));
                assert_eq!(span.shrink(2), Span::new(6, 6));
    }
    #[test]
    #[should_panic(expected = "Cannot shrink span past zero length")]
    fn test_shrink_past_start() {
        let span = Span::new(5, 10);
        let _ = span.shrink(5);
    }
    #[test]
    fn test_move_left() {
        let span = Span::new(5, 10);
        assert_eq!(span.move_left(1), Span::new(4, 9));
        assert_eq!(span.move_left(2), Span::new(3, 8));
        assert_eq!(span.move_left(5), Span::new(0, 5));
    }
    #[test]
    #[should_panic(expected = "Cannot move span past zero length")]
    fn test_move_past_start() {
        let span = Span::new(5, 10);
        let _ = span.move_left(6);
    }
    #[test]
    fn test_move_right() {
        let span: Span = Span::new(5, 10);
        assert_eq!(span.move_right(1), Span::new(6, 11));
        assert_eq!(span.move_right(2), Span::new(7, 12));
        assert_eq!(
            span.move_right(u32::MAX.saturating_sub(10)),
            Span::new(u32::MAX.saturating_sub(5), u32::MAX)
        );
    }
    #[test]
    #[should_panic(expected = "Cannot move span past `u32::MAX` length")]
    fn test_move_past_end() {
        let span = Span::new(u32::MAX.saturating_sub(2), u32::MAX.saturating_sub(1));
        let _ = span.move_right(2);
    }
}
#[cfg(test)]
mod doctests {
    use super::Span;
        #[test]
    fn doctest() {
                let text = "foo bar baz";
        let span = Span::new(4, 7);
        assert_eq!(&text[span], "bar");
                let a = Span::new(5, 10);         let b = Span::sized(5, 5);         assert_eq!(a, b);
                let s = Span::new(5, 10);
        assert_eq!(s.shrink(2), Span::new(7, 8));
        assert_eq!(s.shrink(2), s.shrink_left(2).shrink_right(2));
        assert_eq!(s.expand(5), Span::new(0, 15));
        assert_eq!(s.expand(5), s.expand_left(5).expand_right(5));
    }
}
#[cfg(test)]
mod size_asserts {
    use std::mem::{align_of, size_of};
    use super::Span;
    const _: () = assert!(size_of::<Span>() == 8);
    #[cfg(target_pointer_width = "64")]
    const _: () = assert!(align_of::<Span>() == 8);
    #[cfg(not(target_pointer_width = "64"))]
    const _: () = assert!(align_of::<Span>() == 4);
}