use heapless::Vec;
use alloc::{format, string::String};

pub fn run(log_list: &Vec<String, 12>) -> isize {
    let mut prev = eadkp::input::KeyboardState::scan();
    let line_height = eadkp::SMALL_FONT.height as usize + 2;
    let max_chars = (eadkp::SCREEN_RECT.width as usize / eadkp::SMALL_FONT.width as usize).max(4);
    let max_lines = ((eadkp::SCREEN_RECT.height as usize).saturating_sub(10) / line_height).max(1);
    let mut selected_index: Option<usize> = None;
    let mut scroll_start: usize = 0;
    let mut show_caret = false;
    let mut dirty = true;
    let mut h_offset: usize = 0;
    let mut h_offsets: Vec<usize, 12> = Vec::new();
    let mut last_status = String::new();
    let count_rows = |msg: &str| -> usize {
        let mut rows = 1usize;
        for b in msg.as_bytes() {
            if *b == b'\n' {
                rows += 1;
            }
        }
        rows
    };
    let max_line_len = |msg: &str| -> usize {
        let mut max_len = 0usize;
        let mut current = 0usize;
        for ch in msg.chars() {
            if ch == '\n' {
                if current > max_len {
                    max_len = current;
                }
                current = 0;
            } else {
                current += 1;
            }
        }
        if current > max_len {
            max_len = current;
        }
        max_len
    };

    while h_offsets.len() < log_list.len() {
        let _ = h_offsets.push(0);
    }

    loop {
        let now = eadkp::input::KeyboardState::scan();
        let just = now.get_just_pressed(prev);
        if just.key_down(eadkp::input::Key::Home) {
            break 0;
        };
        let up = just.key_down(eadkp::input::Key::Up);
        let down = just.key_down(eadkp::input::Key::Down);
        let left = just.key_down(eadkp::input::Key::Left);
        let right = just.key_down(eadkp::input::Key::Right);

        let old_selected = selected_index;
        let old_scroll_start = scroll_start;
        let old_show_caret = show_caret;
        let old_h_offset = h_offset;

        let total_lines = log_list.len();
        let mut new_selected = selected_index;
        let mut new_scroll_start = scroll_start;
        let mut new_show_caret: bool;
        let mut available_lines = max_lines;

        while h_offsets.len() < total_lines {
            let _ = h_offsets.push(0);
        }

        if total_lines == 0 {
            new_selected = None;
            new_scroll_start = 0;
            h_offset = 0;
        }

        if up && total_lines > 0 {
            new_selected = match new_selected {
                None => Some(total_lines - 1),
                Some(0) => Some(0),
                Some(i) => Some(i - 1),
            };
        }

        if down {
            if let Some(i) = new_selected {
                if i + 1 < total_lines {
                    new_selected = Some(i + 1);
                }
            }
        }

        if let Some(_) = new_selected {
            if left {
                h_offset = h_offset.saturating_sub(1);
            }

            if right {
                h_offset = h_offset.saturating_add(1);
            }
        }

        let adjust_scroll = |selected: Option<usize>, scroll_start: &mut usize, available_rows: usize| {
            let total = log_list.len();
            if total == 0 {
                *scroll_start = 0;
                return;
            }

            if let Some(sel) = selected {
                if sel < *scroll_start {
                    *scroll_start = sel;
                    return;
                }

                let mut start = *scroll_start;
                loop {
                    let mut rows = 0usize;
                    for idx in start..=sel {
                        if let Some(msg) = log_list.get(idx) {
                            rows += count_rows(msg);
                        }
                    }
                    if rows <= available_rows || start >= sel {
                        break;
                    }
                    start += 1;
                }
                *scroll_start = start;
            } else {
                let mut start = total;
                let mut rows = 0usize;
                while start > 0 {
                    let idx = start - 1;
                    if let Some(msg) = log_list.get(idx) {
                        rows += count_rows(msg);
                    }
                    start -= 1;
                    if rows >= available_rows {
                        break;
                    }
                }
                *scroll_start = start;
            }
        };

        adjust_scroll(new_selected, &mut new_scroll_start, available_lines);

        new_show_caret = new_scroll_start > 0;
        if new_show_caret && max_lines > 1 {
            available_lines = max_lines - 1;
            adjust_scroll(new_selected, &mut new_scroll_start, available_lines);
            new_show_caret = new_scroll_start > 0;
        }

        if new_selected != selected_index {
            if let Some(old) = selected_index {
                if old < h_offsets.len() {
                    h_offsets[old] = h_offset;
                }
            }
            if let Some(i) = new_selected {
                h_offset = if i < h_offsets.len() { h_offsets[i] } else { 0 };
            } else {
                h_offset = 0;
            }
        }

        if let Some(i) = new_selected {
            if let Some(msg) = log_list.get(i) {
                let max_len = max_line_len(msg);
                let max_offset = max_len.saturating_sub(max_chars.saturating_sub(3));
                if max_len <= max_chars {
                    h_offset = 0;
                } else if h_offset > max_offset {
                    h_offset = max_offset;
                }
            }
            if i < h_offsets.len() {
                h_offsets[i] = h_offset;
            }
        }

        let selection_bg = eadkp::Color::from_888(230, 230, 230);
        let y_base = if new_show_caret { 10 + line_height } else { 10 };
        let hscroll_changed = h_offset != old_h_offset;
        let selection_changed = new_selected != old_selected;
        let full_redraw = new_scroll_start != old_scroll_start
            || new_show_caret != old_show_caret
            || dirty
            || selection_changed
            || hscroll_changed;
        let mut status = String::new();
        if let Some(i) = new_selected {
            status = format!("{}/{}", i + 1, total_lines);
        }
        let status_changed = status != last_status;

        selected_index = new_selected;
        scroll_start = new_scroll_start;
        show_caret = new_show_caret;
        dirty = false;

        let draw_segment = |segment: &str, y: usize, is_selected: bool, use_offset: usize| {
            let bg = if is_selected { selection_bg } else { eadkp::COLOR_WHITE };
            let mut line = String::new();
            let mut show_left = false;
            let mut show_right = false;

            let line_len = segment.chars().count();
            if line_len <= max_chars {
                line.push_str(segment);
            } else {
                let max_offset = line_len.saturating_sub(max_chars.saturating_sub(3));
                let use_offset = use_offset.min(max_offset);
                let left_ellipsis = use_offset > 0;
                let right_ellipsis = use_offset + max_chars < line_len;
                let mut content_width = max_chars;
                if left_ellipsis {
                    content_width = content_width.saturating_sub(1);
                    line.push(' ');
                }
                if right_ellipsis {
                    content_width = content_width.saturating_sub(3);
                }

                let mut count = 0usize;
                for (i, ch) in segment.chars().enumerate() {
                    if i < use_offset {
                        continue;
                    }
                    if count >= content_width {
                        break;
                    }
                    line.push(ch);
                    count += 1;
                }

                if right_ellipsis {
                    line.push_str("...");
                }

                if is_selected {
                    show_left = left_ellipsis;
                    show_right = right_ellipsis;
                }
            }

            eadkp::display::push_rect_uniform(
                eadkp::Rect {
                    x: 0,
                    y: y as u16,
                    width: eadkp::SCREEN_RECT.width,
                    height: line_height as u16,
                },
                bg,
            );
            eadkp::display::draw_string(
                line.as_str(),
                eadkp::Point { y: y as u16, x: 5 },
                false,
                eadkp::COLOR_BLACK,
                bg,
            );

            if is_selected && show_left {
                eadkp::display::draw_string(
                    "<",
                    eadkp::Point { y: y as u16, x: 0 },
                    false,
                    eadkp::COLOR_BLACK,
                    bg,
                );
            }
            if is_selected && show_right {
                let x = eadkp::SCREEN_RECT.width.saturating_sub(eadkp::SMALL_FONT.width) as u16;
                eadkp::display::draw_string(
                    ">",
                    eadkp::Point { y: y as u16, x },
                    false,
                    eadkp::COLOR_BLACK,
                    bg,
                );
            }
        };

        let render_visible = || {
            let mut y = y_base;
            let mut rows_left = available_lines;
            let mut index = scroll_start;

            while index < total_lines && rows_left > 0 {
                if let Some(msg) = log_list.get(index) {
                    let is_selected = selected_index == Some(index);
                    let use_offset = if index < h_offsets.len() { h_offsets[index] } else { 0 };
                    let mut start = 0usize;

                    for (i, b) in msg.as_bytes().iter().enumerate() {
                        if *b == b'\n' {
                            let segment = &msg[start..i];
                            draw_segment(segment, y, is_selected, use_offset);
                            y += line_height;
                            rows_left = rows_left.saturating_sub(1);
                            start = i + 1;
                            if rows_left == 0 {
                                break;
                            }
                        }
                    }

                    if rows_left > 0 {
                        let segment = &msg[start..];
                        draw_segment(segment, y, is_selected, use_offset);
                        y += line_height;
                        rows_left = rows_left.saturating_sub(1);
                    }
                }

                index += 1;
            }

            while rows_left > 0 {
                eadkp::display::push_rect_uniform(
                    eadkp::Rect {
                        x: 0,
                        y: y as u16,
                        width: eadkp::SCREEN_RECT.width,
                        height: line_height as u16,
                    },
                    eadkp::COLOR_WHITE,
                );
                y += line_height;
                rows_left -= 1;
            }
        };

        if full_redraw {
            if show_caret {
                eadkp::display::draw_string(
                    "^",
                    eadkp::Point { y: 2, x: 5 },
                    false,
                    eadkp::COLOR_BLACK,
                    eadkp::COLOR_WHITE,
                );
            } else {
                eadkp::display::push_rect_uniform(
                    eadkp::Rect {
                        x: 0,
                        y: 0,
                        width: eadkp::SCREEN_RECT.width,
                        height: line_height as u16,
                    },
                    eadkp::COLOR_WHITE,
                );
            }

            if !status.is_empty() {
                let status_x = eadkp::SCREEN_RECT.width as usize
                    - (status.chars().count() * eadkp::SMALL_FONT.width as usize)
                    - 2;
                eadkp::display::draw_string(
                    status.as_str(),
                    eadkp::Point { y: 2, x: status_x as u16 },
                    false,
                    eadkp::COLOR_BLACK,
                    eadkp::COLOR_WHITE,
                );
            }

            render_visible();
        } else {
            if status_changed || show_caret != old_show_caret {
                let status_width = (last_status.chars().count() * eadkp::SMALL_FONT.width as usize) as u16;
                let status_x = eadkp::SCREEN_RECT.width.saturating_sub(status_width) as u16;
                eadkp::display::push_rect_uniform(
                    eadkp::Rect {
                        x: status_x,
                        y: 0,
                        width: (status_width + 4).min(eadkp::SCREEN_RECT.width),
                        height: line_height as u16,
                    },
                    eadkp::COLOR_WHITE,
                );
                if !status.is_empty() {
                    let x = eadkp::SCREEN_RECT.width as usize
                        - (status.chars().count() * eadkp::SMALL_FONT.width as usize)
                        - 2;
                    eadkp::display::draw_string(
                        status.as_str(),
                        eadkp::Point { y: 2, x: x as u16 },
                        false,
                        eadkp::COLOR_BLACK,
                        eadkp::COLOR_WHITE,
                    );
                }
            }
        }

        last_status = status;

        prev = now;
    }
}
