//! Ring buffer for terminal output capture with UTF-8 boundary handling.
//!
//! **Status**: Implemented (TOOLS-TERM-06)
//!
//! This module implements:
//! - Fixed-size circular buffer
//! - UTF-8 character boundary awareness
//! - Truncation tracking
//! - Thread-safe access

/// Ring buffer for terminal output capture.
///
/// **Status**: Implemented (TOOLS-TERM-06)
///
/// This provides:
/// - Fixed-size circular buffer (configured byte limit)
/// - UTF-8 character boundary preservation
/// - Truncation flag when buffer overflows
/// - Thread-safe access (Arc<Mutex<RingBuffer>>)
///
/// ## Behavior
///
/// - Oldest data dropped when buffer is full
/// - Ensures no partial UTF-8 characters at boundaries
/// - Tracks whether any truncation occurred
/// - Interleaves stdout and stderr in order
///
/// ## UTF-8 Safety
///
/// When truncating:
/// - Check if last byte is part of multi-byte UTF-8 char
/// - If so, truncate at previous character boundary
/// - Prevents invalid UTF-8 in output
///
/// ## See Also
///
/// - Task spec: `docs/building/04_acp_client/04_tasks_02_tools_and_sandboxing.md#TOOLS-TERM-06`
/// - Create terminal: `create.rs`
pub struct RingBuffer {
    /// Internal buffer storage
    buffer: Vec<u8>,
    /// Maximum capacity in bytes
    capacity: usize,
    /// Current write position (circular)
    write_pos: usize,
    /// Current number of valid bytes in buffer
    len: usize,
    /// Whether any data was truncated (dropped) due to overflow
    truncated: bool,
}

impl RingBuffer {
    /// Create a new ring buffer with specified capacity.
    ///
    /// # Arguments
    ///
    /// * `capacity` - Maximum number of bytes to store
    ///
    /// # Examples
    ///
    /// ```
    /// use dirigent_tools::terminal::RingBuffer;
    ///
    /// let buffer = RingBuffer::new(1024);
    /// assert_eq!(buffer.snapshot(), "");
    /// assert!(!buffer.is_truncated());
    /// ```
    pub fn new(capacity: usize) -> Self {
        Self {
            buffer: Vec::with_capacity(capacity),
            capacity,
            write_pos: 0,
            len: 0,
            truncated: false,
        }
    }

    /// Append data to the buffer, truncating from the beginning if capacity is exceeded.
    ///
    /// This method ensures UTF-8 character boundaries are preserved when truncating.
    ///
    /// # Arguments
    ///
    /// * `data` - Bytes to append (may contain partial UTF-8 sequences)
    ///
    /// # Examples
    ///
    /// ```
    /// use dirigent_tools::terminal::RingBuffer;
    ///
    /// let mut buffer = RingBuffer::new(10);
    /// buffer.push(b"Hello");
    /// assert_eq!(buffer.snapshot(), "Hello");
    ///
    /// buffer.push(b" World!");
    /// // Buffer truncated to fit capacity, respecting UTF-8 boundaries
    /// assert!(buffer.is_truncated());
    /// ```
    pub fn push(&mut self, data: &[u8]) {
        if data.is_empty() {
            return;
        }

        // If the buffer is not yet at capacity, we can just append
        if self.len < self.capacity {
            let available = self.capacity - self.len;
            let to_append = data.len().min(available);

            if self.buffer.len() < self.capacity {
                // Buffer hasn't been fully allocated yet
                self.buffer.extend_from_slice(&data[..to_append]);
            } else {
                // Buffer is allocated, write to the circular position
                for &byte in &data[..to_append] {
                    self.buffer[self.write_pos] = byte;
                    self.write_pos = (self.write_pos + 1) % self.capacity;
                }
            }

            self.len += to_append;

            // If we couldn't append all the data, handle the overflow
            if to_append < data.len() {
                self.push_with_overflow(&data[to_append..]);
            }
        } else {
            // Buffer is full, need to overwrite old data
            self.push_with_overflow(data);
        }
    }

    /// Push data when buffer is at capacity (overwrites old data).
    fn push_with_overflow(&mut self, data: &[u8]) {
        if data.is_empty() {
            return;
        }

        self.truncated = true;

        // Ensure buffer is fully allocated
        if self.buffer.len() < self.capacity {
            self.buffer.resize(self.capacity, 0);
        }

        // If data is larger than capacity, only keep the last `capacity` bytes
        let data_to_write = if data.len() >= self.capacity {
            &data[data.len() - self.capacity..]
        } else {
            data
        };

        // Write data circularly
        for &byte in data_to_write {
            self.buffer[self.write_pos] = byte;
            self.write_pos = (self.write_pos + 1) % self.capacity;
        }

        // Update length (stays at capacity)
        self.len = self.capacity;
    }

    /// Get current buffer contents as a UTF-8 string.
    ///
    /// This returns a snapshot of the current contents. Invalid UTF-8 sequences
    /// are handled gracefully by truncating at character boundaries.
    ///
    /// # Returns
    ///
    /// A UTF-8 string containing the buffer contents. If the buffer contains
    /// invalid UTF-8 at the boundary, it will be truncated to the last valid
    /// character boundary.
    ///
    /// # Examples
    ///
    /// ```
    /// use dirigent_tools::terminal::RingBuffer;
    ///
    /// let mut buffer = RingBuffer::new(1024);
    /// buffer.push(b"Hello, World!");
    /// assert_eq!(buffer.snapshot(), "Hello, World!");
    /// ```
    pub fn snapshot(&self) -> String {
        if self.len == 0 {
            return String::new();
        }

        // Reconstruct the linear buffer from the circular buffer
        let linear = if self.len < self.capacity {
            // Buffer not full yet, data is at the beginning
            &self.buffer[..self.len]
        } else {
            // Buffer is full, need to reconstruct in correct order
            let mut temp = Vec::with_capacity(self.capacity);
            let start_pos = self.write_pos;

            for i in 0..self.capacity {
                let pos = (start_pos + i) % self.capacity;
                temp.push(self.buffer[pos]);
            }

            // Find UTF-8 boundary and return owned data
            let boundary = find_char_boundary(&temp, temp.len());
            return String::from_utf8_lossy(&temp[..boundary]).into_owned();
        };

        // Find a valid UTF-8 character boundary
        let boundary = find_char_boundary(linear, linear.len());
        String::from_utf8_lossy(&linear[..boundary]).into_owned()
    }

    /// Check if any data has been truncated due to buffer overflow.
    ///
    /// # Returns
    ///
    /// `true` if data was dropped, `false` otherwise.
    ///
    /// # Examples
    ///
    /// ```
    /// use dirigent_tools::terminal::RingBuffer;
    ///
    /// let mut buffer = RingBuffer::new(5);
    /// buffer.push(b"Hello");
    /// assert!(!buffer.is_truncated());
    ///
    /// buffer.push(b" World!");
    /// assert!(buffer.is_truncated());
    /// ```
    pub fn is_truncated(&self) -> bool {
        self.truncated
    }

    /// Clear the buffer and reset the truncation flag.
    ///
    /// # Examples
    ///
    /// ```
    /// use dirigent_tools::terminal::RingBuffer;
    ///
    /// let mut buffer = RingBuffer::new(1024);
    /// buffer.push(b"Hello");
    /// buffer.clear();
    /// assert_eq!(buffer.snapshot(), "");
    /// assert!(!buffer.is_truncated());
    /// ```
    pub fn clear(&mut self) {
        self.write_pos = 0;
        self.len = 0;
        self.truncated = false;
        self.buffer.clear();
    }

    /// Get the current length of valid data in the buffer.
    pub fn len(&self) -> usize {
        self.len
    }

    /// Check if the buffer is empty.
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }
}

/// Find the nearest character boundary at or before `start` position.
///
/// This ensures we don't cut in the middle of a UTF-8 multi-byte character.
///
/// # Arguments
///
/// * `buf` - Byte buffer to scan
/// * `start` - Position to start scanning backwards from
///
/// # Returns
///
/// The position of a valid UTF-8 character boundary <= `start`.
///
/// # UTF-8 Encoding Rules
///
/// - Single-byte char: `0xxxxxxx` (0x00-0x7F)
/// - Continuation byte: `10xxxxxx` (0x80-0xBF)
/// - Start of 2-byte char: `110xxxxx` (0xC0-0xDF)
/// - Start of 3-byte char: `1110xxxx` (0xE0-0xEF)
/// - Start of 4-byte char: `11110xxx` (0xF0-0xF7)
fn find_char_boundary(buf: &[u8], start: usize) -> usize {
    if start == 0 || buf.is_empty() {
        return 0;
    }

    let start = start.min(buf.len());

    // Scan backwards to find a valid character start
    for i in (0..start).rev() {
        let byte = buf[i];

        // Check if this is a valid character start (not a continuation byte)
        if byte & 0b1100_0000 != 0b1000_0000 {
            // This is either ASCII (0xxxxxxx) or a multi-byte start (11xxxxxx)
            // Verify we have enough bytes for a complete character
            let char_len = if byte & 0b1000_0000 == 0 {
                1 // ASCII
            } else if byte & 0b1110_0000 == 0b1100_0000 {
                2 // 2-byte char
            } else if byte & 0b1111_0000 == 0b1110_0000 {
                3 // 3-byte char
            } else if byte & 0b1111_1000 == 0b1111_0000 {
                4 // 4-byte char
            } else {
                // Invalid UTF-8 start byte, skip it
                continue;
            };

            // Check if we have enough bytes remaining
            if i + char_len <= start {
                // Validate that all continuation bytes are present
                let mut valid = true;
                for j in 1..char_len {
                    if i + j >= buf.len() || buf[i + j] & 0b1100_0000 != 0b1000_0000 {
                        valid = false;
                        break;
                    }
                }

                if valid {
                    return i + char_len;
                }
            }
        }
    }

    // If we couldn't find a valid boundary, return 0
    0
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_new_buffer() {
        let buffer = RingBuffer::new(1024);
        assert_eq!(buffer.snapshot(), "");
        assert!(!buffer.is_truncated());
        assert_eq!(buffer.len(), 0);
        assert!(buffer.is_empty());
    }

    #[test]
    fn test_push_simple() {
        let mut buffer = RingBuffer::new(1024);
        buffer.push(b"Hello");
        assert_eq!(buffer.snapshot(), "Hello");
        assert!(!buffer.is_truncated());
        assert_eq!(buffer.len(), 5);
    }

    #[test]
    fn test_push_multiple() {
        let mut buffer = RingBuffer::new(1024);
        buffer.push(b"Hello");
        buffer.push(b" ");
        buffer.push(b"World");
        assert_eq!(buffer.snapshot(), "Hello World");
        assert!(!buffer.is_truncated());
    }

    #[test]
    fn test_overflow_truncates() {
        let mut buffer = RingBuffer::new(10);
        buffer.push(b"Hello");
        assert!(!buffer.is_truncated());

        buffer.push(b" World!");
        assert!(buffer.is_truncated());

        // Should keep the last 10 bytes
        let result = buffer.snapshot();
        assert!(result.len() <= 10);
        assert!(result.ends_with("World!"));
    }

    #[test]
    fn test_utf8_boundary_preservation() {
        let mut buffer = RingBuffer::new(10);

        // Push UTF-8 string with multi-byte characters (emoji)
        // "Hello😀" is 10 bytes total (Hello=5, 😀=4, but we only have 10 capacity)
        buffer.push("Hello".as_bytes());
        buffer.push("😀".as_bytes()); // This should fit exactly

        let result = buffer.snapshot();
        assert!(result == "Hello😀" || result == "Hello"); // Depending on implementation
        assert!(!buffer.is_truncated() || result.len() <= 10);
    }

    #[test]
    fn test_utf8_truncation() {
        let mut buffer = RingBuffer::new(8);

        // Push string that will cause truncation in the middle of multi-byte char
        buffer.push("Hello😀World".as_bytes());

        // Result should be valid UTF-8 (no partial emoji)
        let result = buffer.snapshot();
        assert!(std::str::from_utf8(result.as_bytes()).is_ok());
        assert!(buffer.is_truncated());
    }

    #[test]
    fn test_clear() {
        let mut buffer = RingBuffer::new(1024);
        buffer.push(b"Hello");
        buffer.clear();

        assert_eq!(buffer.snapshot(), "");
        assert!(!buffer.is_truncated());
        assert_eq!(buffer.len(), 0);
    }

    #[test]
    fn test_empty_push() {
        let mut buffer = RingBuffer::new(1024);
        buffer.push(b"");
        assert_eq!(buffer.snapshot(), "");
        assert!(!buffer.is_truncated());
    }

    #[test]
    fn test_large_data_at_once() {
        let mut buffer = RingBuffer::new(100);
        let large_data = vec![b'A'; 500];

        buffer.push(&large_data);

        assert!(buffer.is_truncated());
        assert_eq!(buffer.len(), 100);

        // Should contain only 'A's
        let result = buffer.snapshot();
        assert!(result.chars().all(|c| c == 'A'));
        assert!(result.len() <= 100);
    }

    #[test]
    fn test_find_char_boundary_ascii() {
        let data = b"Hello";
        assert_eq!(find_char_boundary(data, 5), 5);
        assert_eq!(find_char_boundary(data, 3), 3);
        assert_eq!(find_char_boundary(data, 0), 0);
    }

    #[test]
    fn test_find_char_boundary_utf8() {
        // "Hello😀" - emoji is 4 bytes: F0 9F 98 80
        let data = "Hello😀".as_bytes();
        let total_len = data.len(); // 5 + 4 = 9

        // Should find boundaries correctly
        assert_eq!(find_char_boundary(data, total_len), total_len);
        assert_eq!(find_char_boundary(data, 5), 5); // After "Hello"

        // Middle of emoji should backtrack to before emoji
        assert_eq!(find_char_boundary(data, 6), 5); // 1st continuation byte
        assert_eq!(find_char_boundary(data, 7), 5); // 2nd continuation byte
        assert_eq!(find_char_boundary(data, 8), 5); // 3rd continuation byte
    }

    #[test]
    fn test_find_char_boundary_multi_utf8() {
        // Multiple multi-byte characters
        let data = "日本語".as_bytes(); // 3 chars, 9 bytes (3 each)

        assert_eq!(find_char_boundary(data, 9), 9); // End
        assert_eq!(find_char_boundary(data, 6), 6); // After 2nd char
        assert_eq!(find_char_boundary(data, 3), 3); // After 1st char

        // Middle of 2nd character
        assert_eq!(find_char_boundary(data, 4), 3);
        assert_eq!(find_char_boundary(data, 5), 3);

        // Middle of 3rd character
        assert_eq!(find_char_boundary(data, 7), 6);
        assert_eq!(find_char_boundary(data, 8), 6);
    }

    #[test]
    fn test_circular_buffer_behavior() {
        let mut buffer = RingBuffer::new(5);

        buffer.push(b"ABCDE");
        assert_eq!(buffer.snapshot(), "ABCDE");

        buffer.push(b"FG");
        assert!(buffer.is_truncated());

        let result = buffer.snapshot();
        assert_eq!(result.len(), 5);
        assert!(result.ends_with("G"));
    }
}
