use crate::Location;
use std::{iter::Peekable, str::Chars};

/// Cursor for characters in a string, providing direct value access and advanced peeking.
#[derive(Debug)]
pub struct Cursor<'a> {
    chars: Peekable<Chars<'a>>,
    location: Location,
    value: Option<char>,
}

impl<'a> Cursor<'a> {
    /// Create a new cursor given a string.
    ///
    /// The cursor is initialized with the first character of the string.
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "Hello".to_string();
    /// let c = Cursor::new(&s);
    ///
    /// assert_eq!(c.value(), Some('H'));
    /// assert_eq!(c.index(), 0);
    /// assert_eq!(c.peek(), Some('e'));
    /// ```
    #[must_use]
    pub fn new(line: &'a str) -> Self {
        let mut chars = line.chars();
        let value = chars.next();

        Cursor {
            chars: chars.peekable(),
            location: Location::default(),
            value,
        }
    }

    /// Get the value of the current character the cursor is pointing at (if any).
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "ab".to_string();
    /// let mut c = Cursor::new(&s);
    ///
    /// assert_eq!(c.value(), Some('a'));
    /// c.advance();
    /// assert_eq!(c.value(), Some('b'));
    /// c.advance();
    /// assert_eq!(c.value(), None);
    /// ```
    #[must_use]
    pub fn value(&self) -> Option<char> {
        self.value
    }

    /// Advance the cursor one location, replacing the inner value.
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "abc".to_string();
    /// let mut c = Cursor::new(&s);
    ///
    /// assert_eq!(c.value(), Some('a'));
    /// c.advance();
    /// assert_eq!(c.value(), Some('b'));
    /// c.advance();
    /// assert_eq!(c.value(), Some('c'));
    /// c.advance();
    /// assert_eq!(c.value(), None);
    /// ```
    pub fn advance(&mut self) {
        self.index_inc();
        self.next();
    }

    /// Advance the cursor by N location, consuming the value at each increment.
    ///
    /// If N is zero, `advance_by` is a no-op.
    /// It is a valid operation to advance the cursor beyond the end.
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "abcdefg".to_string();
    /// let mut c = Cursor::new(&s);
    ///
    /// assert_eq!(c.value(), Some('a'));
    /// c.advance_by(3);
    /// assert_eq!(c.value(), Some('d'));
    /// c.advance_by(10);
    /// assert_eq!(c.value(), None);
    /// ```
    pub fn advance_by(&mut self, n: usize) {
        if n != 0 {
            for _ in 0..(n - 1) {
                self.index_inc();
                self.next();

                if self.chars.peek().is_none() {
                    break;
                }
            }

            self.index_inc();
            self.next();
        }
    }

    /// Advance the cursor while a predicate holds, consuming values at each increment.
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "abcd0123".to_string();
    /// let mut c = Cursor::new(&s);
    ///
    /// assert_eq!(c.value(), Some('a'));
    /// c.advance_while(|c| c.is_alphabetic());
    /// assert_eq!(c.value(), Some('0'));
    /// c.advance_while(|c| c.is_numeric());
    /// assert_eq!(c.value(), None);
    /// ```
    pub fn advance_while(&mut self, mut predicate: impl FnMut(char) -> bool) {
        while let Some(c) = self.value {
            if !predicate(c) {
                break;
            }

            self.index_inc();
            self.next();
        }
    }

    /// Peek into the next character without consuming the current value.
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "ab".to_string();
    /// let mut c = Cursor::new(&s);
    ///
    /// assert_eq!(c.value(), Some('a'));
    /// assert_eq!(c.peek(), Some('b'));
    /// c.advance();
    /// assert_eq!(c.value(), Some('b'));
    /// assert_eq!(c.peek(), None);
    /// ```
    #[must_use]
    pub fn peek(&self) -> Option<char> {
        self.chars.clone().peek().copied()
    }

    /// Peek into the next nth character without consuming the current value.
    ///
    /// Returns the current cursor value if 'n' is zero.
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "abc".to_string();
    /// let c = Cursor::new(&s);
    ///
    /// assert_eq!(c.value(), Some('a'));
    /// assert_eq!(c.peek_nth(0), Some('a'));
    /// assert_eq!(c.peek_nth(1), Some('b'));
    /// assert_eq!(c.peek_nth(2), Some('c'));
    /// ```
    #[must_use]
    pub fn peek_nth(&self, n: usize) -> Option<char> {
        match n {
            0 => self.value(),
            1 => self.peek(),
            _ => {
                let mut chars = self.chars.clone();
                for _ in 0..(n - 1) {
                    chars.next();
                }

                chars.peek().copied()
            }
        }
    }

    /// Peek while a predicated holds without consuming the current value.
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "abc123 def".to_string();
    /// let c = Cursor::new(&s);
    ///
    /// assert_eq!(c.peek_while(|c| c.is_alphanumeric()), Some("abc123".to_string()));
    /// ```
    #[must_use]
    pub fn peek_while(&self, mut predicate: impl FnMut(char) -> bool) -> Option<String> {
        let current = self.value?;
        let mut result = String::from(current);

        if !self.at_end() {
            result.extend(self.chars.clone().take_while(|c| predicate(*c)));
        }

        Some(result)
    }

    /// Get the current location of the cursor.
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::Location;
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "abc123\nblah".to_string();
    /// let mut c = Cursor::new(&s);
    ///
    /// assert_eq!(c.location(), Location::new(0, 1, 1));
    /// c.advance();
    /// assert_eq!(c.location(), Location::new(1, 1, 2));
    /// c.advance_by(3);
    /// assert_eq!(c.location(), Location::new(4, 1, 5));
    /// c.advance_by(4);
    /// assert_eq!(c.location(), Location::new(8, 2, 2));
    /// ```
    #[must_use]
    pub fn location(&self) -> Location {
        self.location
    }

    /// Get the current character index of the cursor.
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "abcdefg".to_string();
    /// let mut c = Cursor::new(&s);
    ///
    /// assert_eq!(c.index(), 0);
    /// c.advance();
    /// assert_eq!(c.index(), 1);
    /// c.advance_by(3);
    /// assert_eq!(c.index(), 4);
    /// ```
    #[must_use]
    pub fn index(&self) -> usize {
        self.location.index
    }

    /// Check if the cursor is at the end.
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "x".to_string();
    /// let mut c = Cursor::new(&s);
    ///
    /// assert!(!c.at_end());
    /// c.advance();
    /// assert!(c.at_end());
    /// ```
    #[must_use]
    pub fn at_end(&self) -> bool {
        self.value.is_none()
    }

    /// Consume character and update the cursor state.
    fn next(&mut self) {
        self.value = self.chars.next();
    }

    /// Increment index.
    fn index_inc(&mut self) {
        if let Some(c) = self.value {
            // Use byte width so that location.index is always a valid UTF-8 byte offset,
            // which is required for safe &str slicing in the scanner.
            self.location.index += c.len_utf8();

            if c == '\n' {
                self.location.line += 1;
                self.location.column = 1;
            } else {
                self.location.column += 1;
            }
        }
    }

    /// Advance and return the current character.
    ///
    /// This combines `value()` and `advance()` into a single operation.
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "abc".to_string();
    /// let mut c = Cursor::new(&s);
    ///
    /// assert_eq!(c.consume(), Some('a'));
    /// assert_eq!(c.consume(), Some('b'));
    /// assert_eq!(c.consume(), Some('c'));
    /// assert_eq!(c.consume(), None);
    /// ```
    pub fn consume(&mut self) -> Option<char> {
        let current = self.value;
        self.advance();
        current
    }

    /// Advance only if the current character matches the predicate.
    ///
    /// If the predicate returns `true` for the current character, the cursor advances and returns the character.
    /// Otherwise, the cursor remains unchanged and returns `None`.
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "a1b".to_string();
    /// let mut c = Cursor::new(&s);
    ///
    /// assert_eq!(c.consume_if(|ch| ch.is_alphabetic()), Some('a'));
    /// assert_eq!(c.consume_if(|ch| ch.is_alphabetic()), None); // '1' is not alphabetic
    /// assert_eq!(c.value(), Some('1')); // cursor didn't advance
    /// assert_eq!(c.consume_if(|ch| ch.is_numeric()), Some('1'));
    /// assert_eq!(c.consume_if(|ch| ch.is_alphabetic()), Some('b'));
    /// ```
    pub fn consume_if(&mut self, predicate: impl FnOnce(char) -> bool) -> Option<char> {
        if let Some(c) = self.value
            && predicate(c)
        {
            self.advance();
            return Some(c);
        }

        None
    }

    /// Try to consume a specific character.
    ///
    /// If the current character matches the expected character, the cursor advances and returns `true`.
    /// Otherwise, the cursor remains unchanged and returns `false`.
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "abc".to_string();
    /// let mut c = Cursor::new(&s);
    ///
    /// assert!(c.consume_char('a'));
    /// assert!(!c.consume_char('x')); // 'b' != 'x'
    /// assert_eq!(c.value(), Some('b')); // cursor didn't advance
    /// assert!(c.consume_char('b'));
    /// assert!(c.consume_char('c'));
    /// assert!(!c.consume_char('d')); // at end
    /// ```
    pub fn consume_char(&mut self, expected: char) -> bool {
        if self.value == Some(expected) {
            self.advance();
            true
        } else {
            false
        }
    }
}

impl Iterator for Cursor<'_> {
    type Item = char;

    /// Advance the cursor and return the current character.
    ///
    /// This allows the cursor to be used in iterator contexts, making it compatible with standard iterator methods.
    ///
    /// # Example
    ///
    /// ```
    /// use nexus_lang::scanner::cursor::Cursor;
    ///
    /// let s = "abc".to_string();
    /// let c = Cursor::new(&s);
    ///
    /// let chars: Vec<char> = c.collect();
    /// assert_eq!(chars, vec!['a', 'b', 'c']);
    /// ```
    fn next(&mut self) -> Option<Self::Item> {
        self.consume()
    }
}
