//! Syntax highlighter for Storybook DSL
//!
//! Implements iced's Highlighter trait to provide line-by-line syntax
//! highlighting.

use std::ops::Range;

use iced::{
    Color,
    advanced::text::highlighter,
};

use crate::syntax_highlight::{
    token_to_color_contextual,
    tokenize,
};

/// Settings for the Storybook highlighter
#[derive(Debug, Clone, PartialEq)]
pub struct Settings;

/// Storybook syntax highlighter
#[derive(Debug)]
pub struct StorybookHighlighter {
    current_line: usize,
    in_prose_block: bool, // Track if we're inside a prose block
}

impl iced::widget::text::Highlighter for StorybookHighlighter {
    type Highlight = Color;
    type Iterator<'a> = std::vec::IntoIter<(Range<usize>, Self::Highlight)>;
    type Settings = Settings;

    fn new(_settings: &Self::Settings) -> Self {
        Self {
            current_line: 0,
            in_prose_block: false,
        }
    }

    fn update(&mut self, _new_settings: &Self::Settings) {
        // No settings to update
    }

    fn change_line(&mut self, line: usize) {
        self.current_line = line;
        // Reset prose block state when jumping to a different line
        self.in_prose_block = false;
    }

    fn highlight_line(&mut self, line: &str) -> Self::Iterator<'_> {
        let mut highlights = Vec::new();

        // Check if this line starts or ends a prose block
        let trimmed = line.trim_start();

        if trimmed.starts_with("---") {
            // This is a prose marker line
            if self.in_prose_block {
                // Ending a prose block
                self.in_prose_block = false;
            } else {
                // Starting a prose block
                self.in_prose_block = true;
            }
            // Color the entire line as a prose marker (gray)
            if !line.is_empty() {
                highlights.push((0..line.len(), Color::from_rgb8(0x6d, 0x6d, 0x6d)));
            }
        } else if self.in_prose_block {
            // Inside a prose block - render as plain text in peach color
            if !line.is_empty() {
                highlights.push((0..line.len(), Color::from_rgb8(0xff, 0xb8, 0x6c)));
            }
        } else {
            // Regular code - tokenize and highlight
            let tokens = tokenize(line);

            // Track if we're after a colon for context-aware coloring
            let mut after_colon = false;

            for (token, range) in tokens {
                let color = token_to_color_contextual(&token, after_colon);
                highlights.push((range, color));

                // Update context: set after_colon when we see a colon,
                // reset it when we see an identifier (field value) or certain other tokens
                use storybook::syntax::lexer::Token;
                match &token {
                    | Token::Colon => after_colon = true,
                    | Token::Ident(_) |
                    Token::NumberLit(_) |
                    Token::DecimalLit(_) |
                    Token::TextLit(_) |
                    Token::True |
                    Token::False |
                    Token::TimeLit(_) |
                    Token::DurationLit(_) => {
                        // Reset after consuming a value
                        after_colon = false;
                    },
                    // Don't reset for whitespace, commas, or other punctuation
                    | _ => {},
                }
            }
        }

        self.current_line += 1;
        highlights.into_iter()
    }

    fn current_line(&self) -> usize {
        self.current_line
    }
}

/// Convert a highlight (Color) to a Format for rendering
pub fn to_format(color: &Color, _theme: &iced::Theme) -> highlighter::Format<iced::Font> {
    highlighter::Format {
        color: Some(*color),
        font: None,
    }
}
