//! Switch/Toggle widget
//!
//! A toggle switch for boolean values with customizable styles.

use crate::event::{Key, KeyEvent, MouseButton, MouseEvent, MouseEventKind};
use crate::layout::Rect;
use crate::render::{Cell, Modifier};
use crate::style::Color;
use crate::widget::theme::DISABLED_FG;
use crate::widget::traits::{EventResult, Interactive, RenderContext, View, WidgetProps};
use crate::{impl_props_builders, impl_styled_view};

/// Switch style
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum SwitchStyle {
    /// Default style: [●━━━] / [━━━●]
    #[default]
    Default,
    /// iOS style: (●    ) / (    ●)
    IOS,
    /// Material style: ●━━━○ / ○━━━●
    Material,
    /// Text style: \[OFF\] / \[ON\]
    Text,
    /// Emoji style: ❌ / ✅
    Emoji,
    /// Block style: ▓▓░░ / ░░▓▓
    Block,
}

/// Switch widget
pub struct Switch {
    /// Current state
    on: bool,
    /// Label text
    label: Option<String>,
    /// Label position (true = left)
    label_left: bool,
    /// Visual style
    style: SwitchStyle,
    /// Width of switch track
    width: u16,
    /// Focused state
    focused: bool,
    /// Disabled state
    disabled: bool,
    /// On color
    on_color: Color,
    /// Off color
    off_color: Color,
    /// Track color
    track_color: Color,
    /// Custom on text
    on_text: Option<String>,
    /// Custom off text
    off_text: Option<String>,
    props: WidgetProps,
}

impl Switch {
    /// Create a new switch
    pub fn new() -> Self {
        Self {
            on: false,
            label: None,
            label_left: true,
            style: SwitchStyle::Default,
            width: 6,
            focused: false,
            disabled: false,
            on_color: Color::GREEN,
            off_color: DISABLED_FG,
            track_color: Color::rgb(60, 60, 60),
            on_text: None,
            off_text: None,
            props: WidgetProps::new(),
        }
    }

    /// Set initial state
    pub fn on(mut self, on: bool) -> Self {
        self.on = on;
        self
    }

    /// Set initial state (alias for `on()` to match Checkbox API)
    pub fn checked(self, checked: bool) -> Self {
        self.on(checked)
    }

    /// Set label
    pub fn label(mut self, label: impl Into<String>) -> Self {
        self.label = Some(label.into());
        self
    }

    /// Set label on right side
    pub fn label_right(mut self) -> Self {
        self.label_left = false;
        self
    }

    /// Set style
    pub fn style(mut self, style: SwitchStyle) -> Self {
        self.style = style;
        self
    }

    /// Set width
    pub fn width(mut self, width: u16) -> Self {
        self.width = width.max(4);
        self
    }

    /// Set focused state
    pub fn focused(mut self, focused: bool) -> Self {
        self.focused = focused;
        self
    }

    /// Set disabled state
    pub fn disabled(mut self, disabled: bool) -> Self {
        self.disabled = disabled;
        self
    }

    /// Set on color
    pub fn on_color(mut self, color: Color) -> Self {
        self.on_color = color;
        self
    }

    /// Set off color
    pub fn off_color(mut self, color: Color) -> Self {
        self.off_color = color;
        self
    }

    /// Set track color
    pub fn track_color(mut self, color: Color) -> Self {
        self.track_color = color;
        self
    }

    /// Set custom text
    pub fn text(mut self, on: impl Into<String>, off: impl Into<String>) -> Self {
        self.on_text = Some(on.into());
        self.off_text = Some(off.into());
        self
    }

    /// Toggle state
    pub fn toggle(&mut self) {
        if !self.disabled {
            self.on = !self.on;
        }
    }

    /// Set state
    pub fn set(&mut self, on: bool) {
        if !self.disabled {
            self.on = on;
        }
    }

    /// Get current state
    pub fn is_on(&self) -> bool {
        self.on
    }

    /// Get current state (alias for `is_on()` to match Checkbox API)
    pub fn is_checked(&self) -> bool {
        self.is_on()
    }

    /// Handle key input
    pub fn handle_key(&mut self, key: &crate::event::Key) -> bool {
        use crate::event::Key;

        if self.disabled || !self.focused {
            return false;
        }

        match key {
            Key::Enter | Key::Char(' ') => {
                self.toggle();
                true
            }
            _ => false,
        }
    }

    /// Render default style
    fn render_default(&self, ctx: &mut RenderContext, x: u16, y: u16) {
        let color = if self.on {
            self.on_color
        } else {
            self.off_color
        };
        let track_len = self.width.saturating_sub(2);

        // Opening bracket
        let mut open = Cell::new('[');
        open.fg = Some(if self.focused { Color::CYAN } else { color });
        ctx.set(x, y, open);

        // Track
        for i in 0..track_len {
            let is_knob = if self.on { i == track_len - 1 } else { i == 0 };

            let ch = if is_knob { '●' } else { '━' };
            let mut cell = Cell::new(ch);
            cell.fg = Some(if is_knob { color } else { self.track_color });
            ctx.set(x + 1 + i, y, cell);
        }

        // Closing bracket
        let mut close = Cell::new(']');
        close.fg = Some(if self.focused { Color::CYAN } else { color });
        ctx.set(x + self.width - 1, y, close);
    }

    /// Render iOS style
    fn render_ios(&self, ctx: &mut RenderContext, x: u16, y: u16) {
        let color = if self.on {
            self.on_color
        } else {
            self.off_color
        };
        let bg = if self.on {
            self.on_color
        } else {
            self.track_color
        };
        let track_len = self.width.saturating_sub(2);

        // Opening paren
        let mut open = Cell::new('(');
        open.fg = Some(color);
        ctx.set(x, y, open);

        // Track with knob
        for i in 0..track_len {
            let is_knob = if self.on { i == track_len - 1 } else { i == 0 };

            let ch = if is_knob { '●' } else { ' ' };
            let mut cell = Cell::new(ch);
            cell.fg = Some(Color::WHITE);
            cell.bg = Some(bg);
            ctx.set(x + 1 + i, y, cell);
        }

        // Closing paren
        let mut close = Cell::new(')');
        close.fg = Some(color);
        ctx.set(x + self.width - 1, y, close);
    }

    /// Render Material style
    fn render_material(&self, ctx: &mut RenderContext, x: u16, y: u16) {
        let color = if self.on {
            self.on_color
        } else {
            self.off_color
        };
        let track_len = self.width;

        for i in 0..track_len {
            let is_left_knob = i == 0;
            let is_right_knob = i == track_len - 1;

            let (ch, fg) = if self.on {
                if is_right_knob {
                    ('●', color)
                } else if is_left_knob {
                    ('○', self.track_color)
                } else {
                    ('━', color)
                }
            } else if is_left_knob {
                ('●', color)
            } else if is_right_knob {
                ('○', self.track_color)
            } else {
                ('━', self.track_color)
            };

            let mut cell = Cell::new(ch);
            cell.fg = Some(fg);
            ctx.set(x + i, y, cell);
        }
    }

    /// Render text style
    fn render_text(&self, ctx: &mut RenderContext, x: u16, y: u16) {
        let (text, color) = if self.on {
            (self.on_text.as_deref().unwrap_or("ON"), self.on_color)
        } else {
            (self.off_text.as_deref().unwrap_or("OFF"), self.off_color)
        };

        let mut open = Cell::new('[');
        open.fg = Some(if self.focused {
            Color::CYAN
        } else {
            Color::WHITE
        });
        ctx.set(x, y, open);

        for (i, ch) in text.chars().enumerate() {
            let mut cell = Cell::new(ch);
            cell.fg = Some(color);
            if self.on {
                cell.modifier |= Modifier::BOLD;
            }
            ctx.set(x + 1 + i as u16, y, cell);
        }

        let mut close = Cell::new(']');
        close.fg = Some(if self.focused {
            Color::CYAN
        } else {
            Color::WHITE
        });
        ctx.set(x + 1 + text.len() as u16, y, close);
    }

    /// Render emoji style
    fn render_emoji(&self, ctx: &mut RenderContext, x: u16, y: u16) {
        let ch = if self.on { '✅' } else { '❌' };
        let mut cell = Cell::new(ch);
        cell.fg = Some(if self.on {
            self.on_color
        } else {
            self.off_color
        });
        ctx.set(x, y, cell);
        // Wide emoji occupies 2 columns — clear the second cell to prevent artifacts
        ctx.set(x + 1, y, Cell::new(' '));
    }

    /// Render block style
    fn render_block(&self, ctx: &mut RenderContext, x: u16, y: u16) {
        let track_len = self.width;
        let half = track_len / 2;

        for i in 0..track_len {
            let is_filled = if self.on { i >= half } else { i < half };
            let ch = if is_filled { '▓' } else { '░' };
            let color = if self.on {
                self.on_color
            } else {
                self.off_color
            };

            let mut cell = Cell::new(ch);
            cell.fg = Some(color);
            ctx.set(x + i, y, cell);
        }
    }
}

impl Default for Switch {
    fn default() -> Self {
        Self::new()
    }
}

impl View for Switch {
    fn render(&self, ctx: &mut RenderContext) {
        let area = ctx.area;
        if area.width == 0 || area.height == 0 {
            return;
        }

        let mut x: u16 = 0;
        let y: u16 = 0;

        // Render label if on left
        if self.label_left {
            if let Some(ref label) = self.label {
                let label_color = if self.disabled {
                    DISABLED_FG
                } else if let Some(css_fg) = ctx
                    .style
                    .map(|s| s.visual.color)
                    .filter(|c| *c != Color::default())
                {
                    css_fg
                } else {
                    Color::WHITE
                };
                ctx.draw_text_clipped(x, y, label, label_color, area.width.saturating_sub(x));
                x += crate::utils::display_width(label) as u16 + 1;
            }
        }

        // Render switch
        if x < area.width {
            match self.style {
                SwitchStyle::Default => self.render_default(ctx, x, y),
                SwitchStyle::IOS => self.render_ios(ctx, x, y),
                SwitchStyle::Material => self.render_material(ctx, x, y),
                SwitchStyle::Text => self.render_text(ctx, x, y),
                SwitchStyle::Emoji => self.render_emoji(ctx, x, y),
                SwitchStyle::Block => self.render_block(ctx, x, y),
            }

            x += match self.style {
                SwitchStyle::Text => {
                    let text = if self.on {
                        self.on_text.as_deref().unwrap_or("ON")
                    } else {
                        self.off_text.as_deref().unwrap_or("OFF")
                    };
                    text.len() as u16 + 2
                }
                SwitchStyle::Emoji => 2,
                _ => self.width,
            };
        }

        // Render label if on right
        if !self.label_left {
            if let Some(ref label) = self.label {
                x += 1;
                let label_color = if self.disabled {
                    DISABLED_FG
                } else if let Some(css_fg) = ctx
                    .style
                    .map(|s| s.visual.color)
                    .filter(|c| *c != Color::default())
                {
                    css_fg
                } else {
                    Color::WHITE
                };
                ctx.draw_text_clipped(x, y, label, label_color, area.width.saturating_sub(x));
            }
        }

        // Render focus indicator
        if self.focused && !self.disabled {
            // Find switch start position (relative)
            let switch_x = if self.label_left {
                self.label
                    .as_ref()
                    .map(|l| crate::utils::display_width(l) as u16 + 1)
                    .unwrap_or(0)
            } else {
                0u16
            };

            // Draw focus bracket on left
            if switch_x > 0 {
                let mut left = Cell::new('[');
                left.fg = Some(Color::CYAN);
                ctx.set(switch_x.saturating_sub(1), y, left);
            } else {
                let mut left = Cell::new('[');
                left.fg = Some(Color::CYAN);
                ctx.set(0, y, left);
            }

            // Draw focus bracket on right
            let switch_width = match self.style {
                SwitchStyle::Text => {
                    let text = if self.on {
                        self.on_text.as_deref().unwrap_or("ON")
                    } else {
                        self.off_text.as_deref().unwrap_or("OFF")
                    };
                    text.len() as u16 + 2
                }
                SwitchStyle::Emoji => 2,
                _ => self.width,
            };
            let right_x = switch_x + switch_width;
            if right_x < area.width {
                let mut right = Cell::new(']');
                right.fg = Some(Color::CYAN);
                ctx.set(right_x, y, right);
            }
        }
    }

    crate::impl_view_meta!("Switch");
}

impl Interactive for Switch {
    fn handle_key(&mut self, event: &KeyEvent) -> EventResult {
        if self.disabled {
            return EventResult::Ignored;
        }

        match event.key {
            Key::Enter | Key::Char(' ') => {
                self.toggle();
                EventResult::ConsumedAndRender
            }
            _ => EventResult::Ignored,
        }
    }

    fn handle_mouse(&mut self, event: &MouseEvent, _area: Rect) -> EventResult {
        if self.disabled {
            return EventResult::Ignored;
        }

        match event.kind {
            MouseEventKind::Down(MouseButton::Left) => {
                self.toggle();
                EventResult::ConsumedAndRender
            }
            _ => EventResult::Ignored,
        }
    }

    fn focusable(&self) -> bool {
        !self.disabled
    }

    fn on_focus(&mut self) {
        self.focused = true;
    }

    fn on_blur(&mut self) {
        self.focused = false;
    }
}

/// Helper to create a switch
pub fn switch() -> Switch {
    Switch::new()
}

/// Helper to create a labeled switch
pub fn toggle(label: impl Into<String>) -> Switch {
    Switch::new().label(label)
}

impl_styled_view!(Switch);
impl_props_builders!(Switch);

// All tests moved to tests/widget/switch.rs
