use crate::prelude::*;

pub trait ShellHandler {
    fn on_command(&mut self, world: &mut World, command: &str);
}

pub struct ShellConfig {
    pub max_terminal_lines: usize,
    pub font_size: f32,
    pub text_color: Vec4,
    pub cursor_blink_rate: u32,
    pub show_scrollbar: bool,
}

impl Default for ShellConfig {
    fn default() -> Self {
        Self {
            max_terminal_lines: 25,
            font_size: 16.0,
            text_color: Vec4::new(0.0, 1.0, 0.0, 1.0),
            cursor_blink_rate: 30,
            show_scrollbar: true,
        }
    }
}

pub struct Shell {
    config: ShellConfig,
    user_input: String,
    terminal_lines: Vec<String>,
    terminal_entities: Vec<Entity>,
    input_entity: Option<Entity>,
    scrollbar_entity: Option<Entity>,
    cursor_blink_counter: u32,
    scroll_offset: usize,
}

impl Shell {
    pub fn new(config: ShellConfig) -> Self {
        Self {
            config,
            user_input: String::new(),
            terminal_lines: Vec::new(),
            terminal_entities: Vec::new(),
            input_entity: None,
            scrollbar_entity: None,
            cursor_blink_counter: 0,
            scroll_offset: 0,
        }
    }

    pub fn new_with_defaults() -> Self {
        Self::new(ShellConfig::default())
    }

    pub fn initialize(&mut self, world: &mut World) {
        let terminal_props = TextProperties {
            font_size: self.config.font_size,
            color: self.config.text_color,
            alignment: TextAlignment::Left,
            line_height: 1.2,
            ..Default::default()
        };

        let terminal_entity = spawn_hud_text_with_properties(
            world,
            "",
            HudAnchor::TopLeft,
            Vec2::new(20.0, -20.0),
            terminal_props,
        );
        self.terminal_entities.push(terminal_entity);

        let prompt_props = TextProperties {
            font_size: self.config.font_size,
            color: self.config.text_color,
            alignment: TextAlignment::Left,
            ..Default::default()
        };

        self.input_entity = Some(spawn_hud_text_with_properties(
            world,
            "> _",
            HudAnchor::BottomLeft,
            Vec2::new(20.0, -30.0),
            prompt_props,
        ));

        if self.config.show_scrollbar {
            let scrollbar_props = TextProperties {
                font_size: self.config.font_size * 0.75,
                color: Vec4::new(0.5, 0.5, 0.5, 1.0),
                alignment: TextAlignment::Right,
                line_height: 1.0,
                ..Default::default()
            };

            self.scrollbar_entity = Some(spawn_hud_text_with_properties(
                world,
                "",
                HudAnchor::TopRight,
                Vec2::new(-20.0, -20.0),
                scrollbar_props,
            ));
        }

        self.update_terminal_display(world);
    }

    pub fn print_line(&mut self, line: &str) {
        self.terminal_lines.push(line.to_string());
        self.scroll_offset = 0;
    }

    pub fn clear(&mut self) {
        self.terminal_lines.clear();
        self.scroll_offset = 0;
    }

    pub fn get_input(&self) -> &str {
        &self.user_input
    }

    pub fn update(&mut self, world: &mut World) {
        self.update_terminal_display(world);
    }

    pub fn on_keyboard_input(
        &mut self,
        world: &mut World,
        key_code: KeyCode,
        key_state: KeyState,
        handler: &mut impl ShellHandler,
    ) {
        if !matches!(key_state, KeyState::Pressed) {
            return;
        }

        match key_code {
            KeyCode::PageUp | KeyCode::ArrowUp => {
                let total_lines = self.terminal_lines.len();
                let visible_lines = self.config.max_terminal_lines;
                if total_lines > visible_lines {
                    let max_scroll = total_lines - visible_lines;
                    self.scroll_offset = (self.scroll_offset + 1).min(max_scroll);
                }
            }
            KeyCode::PageDown | KeyCode::ArrowDown => {
                if self.scroll_offset > 0 {
                    self.scroll_offset -= 1;
                }
            }
            KeyCode::Home => {
                let total_lines = self.terminal_lines.len();
                let visible_lines = self.config.max_terminal_lines;
                if total_lines > visible_lines {
                    self.scroll_offset = total_lines - visible_lines;
                }
            }
            KeyCode::End => {
                self.scroll_offset = 0;
            }
            KeyCode::Enter => {
                let command = self.user_input.clone();
                self.user_input.clear();
                if !command.is_empty() {
                    self.print_line(&format!("> {}", command));
                    handler.on_command(world, &command);
                }
            }
            KeyCode::Backspace => {
                self.user_input.pop();
            }
            KeyCode::Space => {
                self.user_input.push(' ');
            }
            KeyCode::KeyA => self.user_input.push('a'),
            KeyCode::KeyB => self.user_input.push('b'),
            KeyCode::KeyC => self.user_input.push('c'),
            KeyCode::KeyD => self.user_input.push('d'),
            KeyCode::KeyE => self.user_input.push('e'),
            KeyCode::KeyF => self.user_input.push('f'),
            KeyCode::KeyG => self.user_input.push('g'),
            KeyCode::KeyH => self.user_input.push('h'),
            KeyCode::KeyI => self.user_input.push('i'),
            KeyCode::KeyJ => self.user_input.push('j'),
            KeyCode::KeyK => self.user_input.push('k'),
            KeyCode::KeyL => self.user_input.push('l'),
            KeyCode::KeyM => self.user_input.push('m'),
            KeyCode::KeyN => self.user_input.push('n'),
            KeyCode::KeyO => self.user_input.push('o'),
            KeyCode::KeyP => self.user_input.push('p'),
            KeyCode::KeyQ => self.user_input.push('q'),
            KeyCode::KeyR => self.user_input.push('r'),
            KeyCode::KeyS => self.user_input.push('s'),
            KeyCode::KeyT => self.user_input.push('t'),
            KeyCode::KeyU => self.user_input.push('u'),
            KeyCode::KeyV => self.user_input.push('v'),
            KeyCode::KeyW => self.user_input.push('w'),
            KeyCode::KeyX => self.user_input.push('x'),
            KeyCode::KeyY => self.user_input.push('y'),
            KeyCode::KeyZ => self.user_input.push('z'),
            _ => {}
        }
    }

    fn update_terminal_display(&mut self, world: &mut World) {
        self.cursor_blink_counter += 1;

        let total_lines = self.terminal_lines.len();
        let visible_lines = self.config.max_terminal_lines;

        let end_index = total_lines.saturating_sub(self.scroll_offset);
        let start_index = end_index.saturating_sub(visible_lines);

        let visible_text: Vec<&str> = self.terminal_lines[start_index..end_index]
            .iter()
            .map(|s| s.as_str())
            .collect();
        let terminal_text = visible_text.join("\n");

        for entity in &self.terminal_entities {
            if let Some(hud_text) = world.get_hud_text(*entity) {
                let text_index = hud_text.text_index;
                world
                    .resources
                    .text_cache
                    .set_text(text_index, &terminal_text);
                if let Some(hud_text) = world.get_hud_text_mut(*entity) {
                    hud_text.dirty = true;
                }
            }
        }

        if self.config.show_scrollbar
            && let Some(entity) = self.scrollbar_entity
            && let Some(hud_text) = world.get_hud_text(entity)
        {
            let text_index = hud_text.text_index;

            let scrollbar_text = if total_lines > visible_lines {
                let max_scroll = total_lines - visible_lines;
                let scroll_percent = if max_scroll > 0 {
                    (max_scroll - self.scroll_offset) as f32 / max_scroll as f32
                } else {
                    1.0
                };

                let bar_height = 20;
                let bar_pos = (scroll_percent * (bar_height - 1) as f32) as usize;

                let mut bar_chars = vec!["|"; bar_height];
                bar_chars[bar_pos] = "#";

                format!(
                    "{}\n[{}-{}/{}]",
                    bar_chars.join("\n"),
                    start_index + 1,
                    end_index,
                    total_lines
                )
            } else {
                String::new()
            };

            world
                .resources
                .text_cache
                .set_text(text_index, &scrollbar_text);
            if let Some(hud_text) = world.get_hud_text_mut(entity) {
                hud_text.dirty = true;
            }
        }

        if let Some(entity) = self.input_entity
            && let Some(hud_text) = world.get_hud_text(entity)
        {
            let text_index = hud_text.text_index;
            let cursor =
                if (self.cursor_blink_counter / self.config.cursor_blink_rate).is_multiple_of(2) {
                    "_"
                } else {
                    " "
                };
            let input_text = format!("> {}{}", self.user_input, cursor);
            world.resources.text_cache.set_text(text_index, &input_text);
            if let Some(hud_text) = world.get_hud_text_mut(entity) {
                hud_text.dirty = true;
            }
        }
    }
}
