pub(crate) mod input;
pub(crate) mod message_list;
pub(crate) mod panels;
pub(crate) mod welcome;

use ratatui::Frame;
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use ratatui::style::{Color, Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Paragraph};

use crate::app::App;
use crate::app::Screen;
use crate::tui::render::logo::{logo_fits, logo_lines};
use crate::tui::render::render_statusbar;
use crate::tui::syntax::wrap_text_to_lines;

pub(crate) use message_list::centered_rect;
use message_list::{dim_buffer, render_messages};
use welcome::{render_welcome_logo, render_welcome_tips};

use input::render_command_suggestions;
use panels::{
    render_agent_picker_modal, render_ask_user_in_input_box, render_confirm_modal,
    render_model_picker_modal, render_permissions_in_input_box, render_theme_picker_modal,
    render_todos,
};

pub(crate) fn render_chat(frame: &mut Frame, app: &App) {
    let area = frame.area();
    let suggestions = app.command_suggestions();
    let suggestion_height = if suggestions.is_empty() {
        0
    } else {
        u16::try_from(suggestions.len().min(10)).unwrap_or(u16::MAX)
    };

    let has_todos = !app.chat.todos.is_empty() && !app.chat.messages.is_empty();
    let (left_pane, right_pane, statusbar_area) = if has_todos && area.width >= 50 {
        let sidebar_width = if area.width >= 90 { 40 } else { 34 };
        let horizontal_split = Layout::default()
            .direction(Direction::Horizontal)
            .constraints([Constraint::Min(20), Constraint::Length(sidebar_width)])
            .split(area);
        let left_full_area = horizontal_split[0];
        let right_pane = horizontal_split[1];

        let vertical_split = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Min(1),
                Constraint::Length(1), // Status bar
                Constraint::Length(1), // Bottom spacer (empty line at bottom)
            ])
            .split(left_full_area);

        (vertical_split[0], Some(right_pane), vertical_split[1])
    } else {
        let vertical_split = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Min(1),
                Constraint::Length(1), // Status bar
                Constraint::Length(1), // Bottom spacer (empty line at bottom)
            ])
            .split(area);
        (vertical_split[0], None, vertical_split[1])
    };

    let text_width = if app.chat.messages.is_empty() {
        (left_pane.width as u32 * 70 / 100) as u16
    } else {
        left_pane.width
    }
    .saturating_sub(13)
    .max(1);
    let wrapped_lines = wrap_text_to_lines(app.chat.input.as_str(), text_width as usize);
    let display_lines = u16::try_from(wrapped_lines.len()).unwrap_or(u16::MAX);
    let input_height = if app.ui.screen == Screen::AskUser {
        let q_wrapped = wrap_text_to_lines(app.ui.ask_user.question.as_str(), text_width as usize);
        let q_lines = u16::try_from(q_wrapped.len()).unwrap_or(u16::MAX);
        let opt_count = u16::try_from(app.ui.ask_user.options.len()).unwrap_or(u16::MAX);
        let custom_lines = if app.ui.ask_user.is_custom {
            if app.ui.ask_user.custom_input.is_empty() {
                1
            } else {
                u16::try_from(app.ui.ask_user.custom_input.split('\n').count()).unwrap_or(u16::MAX)
            }
        } else {
            0
        };
        (q_lines + 1 + opt_count + custom_lines + 1 + 1 + 1 + 2).max(6)
    } else if app.ui.screen == Screen::Permissions {
        let opts = crate::app::PermissionPickerState::options();
        let opt_count = u16::try_from(opts.len()).unwrap_or(u16::MAX);
        (1 + 1 + opt_count * 2 + 1 + 1 + 2).max(6)
    } else {
        display_lines.clamp(1, 5) + 4
    };

    let (messages_area, suggestions_area, queue_area, input_area, logo_area, tips_area) =
        if app.chat.messages.is_empty() {
            let active_theme = crate::tui::render::get_active_theme(app);
            let logo_fg = if active_theme.is_light {
                Color::Black
            } else {
                Color::White
            };
            let logo_lines = logo_lines(Style::default().fg(logo_fg));
            let logo_fits_flag = logo_fits(&logo_lines, left_pane.width, 5);

            let remaining_height = left_pane
                .height
                .saturating_sub(input_height + suggestion_height);
            let half_height = remaining_height / 2;
            let top_height = half_height;
            let bottom_height = remaining_height.saturating_sub(half_height);

            let main_chunks = Layout::default()
                .direction(Direction::Vertical)
                .constraints([
                    Constraint::Length(top_height),
                    Constraint::Length(suggestion_height),
                    Constraint::Length(input_height),
                    Constraint::Length(bottom_height),
                ])
                .split(left_pane);

            let logo_spacer = if top_height >= 8 {
                3
            } else if top_height >= 7 {
                2
            } else if top_height >= 6 {
                1
            } else {
                0
            };

            let top_chunks = Layout::default()
                .direction(Direction::Vertical)
                .constraints([
                    Constraint::Min(0),
                    Constraint::Length(5), // Logo (5 lines)
                    Constraint::Length(logo_spacer),
                ])
                .split(main_chunks[0]);

            let bottom_chunks = Layout::default()
                .direction(Direction::Vertical)
                .constraints([
                    Constraint::Length(1), // Spacer
                    Constraint::Length(1), // Tips line
                    Constraint::Min(0),
                ])
                .split(main_chunks[3]);

            let centered_suggestions_box = if suggestion_height > 0 {
                Some(
                    Layout::default()
                        .direction(Direction::Horizontal)
                        .constraints([
                            Constraint::Percentage(15),
                            Constraint::Percentage(70),
                            Constraint::Percentage(15),
                        ])
                        .split(main_chunks[1])[1],
                )
            } else {
                None
            };

            let centered_input_box = Layout::default()
                .direction(Direction::Horizontal)
                .constraints([
                    Constraint::Percentage(15),
                    Constraint::Percentage(70),
                    Constraint::Percentage(15),
                ])
                .split(main_chunks[2])[1];

            let logo_area = if logo_fits_flag && top_height >= 5 {
                Some(top_chunks[1])
            } else {
                None
            };

            let tips_area = if bottom_height >= 2 {
                Some(bottom_chunks[1])
            } else {
                None
            };

            (
                None,
                centered_suggestions_box,
                None,
                Some(centered_input_box),
                logo_area,
                tips_area,
            )
        } else {
            let queue_height = if app.chat.message_queue.is_empty() {
                0
            } else {
                u16::try_from(app.chat.message_queue.len().min(3)).unwrap_or(u16::MAX) + 1
            };

            let spacer_height = if suggestion_height > 0 || queue_height > 0 {
                0
            } else {
                1
            };

            let normal_chunks = Layout::default()
                .direction(Direction::Vertical)
                .constraints([
                    Constraint::Min(4),
                    Constraint::Length(suggestion_height),
                    Constraint::Length(queue_height),
                    Constraint::Length(spacer_height), // dynamic vertical spacer
                    Constraint::Length(input_height),
                ])
                .split(left_pane);

            (
                Some(normal_chunks[0]),
                if suggestion_height > 0 {
                    Some(normal_chunks[1])
                } else {
                    None
                },
                if queue_height > 0 {
                    Some(normal_chunks[2])
                } else {
                    None
                },
                Some(normal_chunks[4]),
                None,
                None,
            )
        };

    if let Some(logo_area) = logo_area {
        render_welcome_logo(frame, app, logo_area);
    }

    if let Some(tips_area) = tips_area {
        render_welcome_tips(frame, app, tips_area);
    }

    if let Some(messages_area) = messages_area {
        if !app.chat.todos.is_empty() && right_pane.is_none() {
            let layout = Layout::default()
                .direction(Direction::Vertical)
                .constraints([Constraint::Min(10), Constraint::Length(8)])
                .split(messages_area);
            render_messages(frame, app, layout[0]);
            render_todos(frame, app, layout[1]);
        } else {
            render_messages(frame, app, messages_area);
        }
    }

    if let Some(suggestions_area) = suggestions_area {
        let margin = 2;
        let flat_suggestions_area = Rect {
            x: suggestions_area.x.saturating_add(margin),
            y: suggestions_area.y,
            width: suggestions_area.width.saturating_sub(margin * 2),
            height: suggestions_area.height,
        };
        render_command_suggestions(frame, app, flat_suggestions_area);
    }

    if let Some(queue_area) = queue_area {
        let mut queue_lines = vec![Line::from(Span::styled(
            " Queued Prompts: ",
            Style::default()
                .fg(Color::DarkGray)
                .add_modifier(Modifier::BOLD),
        ))];
        for (idx, item) in app.chat.message_queue.iter().enumerate().take(3) {
            let truncated_item = if item.chars().count() > 60 {
                let s: String = item.chars().take(57).collect();
                format!("{}...", s)
            } else {
                item.clone()
            };
            queue_lines.push(Line::from(vec![
                Span::styled(
                    format!("  [{}] ", idx + 1),
                    Style::default().fg(Color::DarkGray),
                ),
                Span::styled(truncated_item, Style::default().fg(Color::DarkGray)),
            ]));
        }
        if app.chat.message_queue.len() > 3 {
            let remaining = app.chat.message_queue.len() - 3;
            queue_lines.push(Line::from(Span::styled(
                format!("  ... and {} more", remaining),
                Style::default().fg(Color::DarkGray),
            )));
        }
        frame.render_widget(Paragraph::new(queue_lines), queue_area);
    }

    let input_box = input_area.unwrap();

    let margin = 2;
    let flat_input_area = Rect {
        x: input_box.x.saturating_add(margin),
        y: input_box.y,
        width: input_box.width.saturating_sub(margin * 2),
        height: input_box.height,
    };

    let active_theme = crate::tui::render::get_active_theme(app);
    let border_color = if app.chat.shell_focused {
        active_theme.text_muted
    } else {
        active_theme.primary
    };

    let mut blue_line_lines = Vec::new();
    for _ in 0..input_height {
        blue_line_lines.push(Line::from(Span::styled(
            "┃",
            Style::default().fg(border_color),
        )));
    }
    frame.render_widget(
        Paragraph::new(blue_line_lines),
        Rect {
            x: flat_input_area.x,
            y: flat_input_area.y,
            width: 1,
            height: input_height,
        },
    );

    let text_block_area = Rect {
        x: flat_input_area.x.saturating_add(1),
        y: flat_input_area.y,
        width: flat_input_area.width.saturating_sub(1),
        height: input_height,
    };

    let active_theme = crate::tui::render::get_active_theme(app);
    let bg_color = active_theme.background_element.unwrap_or({
        if active_theme.is_light {
            Color::Rgb(240, 240, 240)
        } else {
            Color::Rgb(24, 24, 24)
        }
    });
    let fg_color = active_theme.text;

    let bg_block = Block::default().style(Style::default().bg(bg_color));
    frame.render_widget(bg_block, text_block_area);

    let inner_x = text_block_area.x.saturating_add(3);
    let inner_y = if app.ui.screen == Screen::AskUser {
        text_block_area.y
    } else {
        text_block_area.y.saturating_add(1)
    };
    let inner_w = if app.ui.screen == Screen::AskUser {
        text_block_area.width.saturating_sub(4)
    } else {
        text_block_area.width.saturating_sub(5) // match the new inner_x and leave 2 right padding
    };
    let inner_h = if app.ui.screen == Screen::AskUser {
        text_block_area.height
    } else {
        text_block_area.height.saturating_sub(1)
    };

    let text_area = if app.ui.screen == Screen::AskUser {
        Rect {
            x: inner_x,
            y: inner_y,
            width: inner_w,
            height: inner_h,
        }
    } else {
        Rect {
            x: inner_x,
            y: inner_y,
            width: inner_w,
            height: inner_h.saturating_sub(3), // leave 1 spacer + 1 mode/model + 1 bottom padding
        }
    };

    let mode_model_row_area = Rect {
        x: inner_x,
        y: inner_y.saturating_add(inner_h.saturating_sub(2)), // 1 spacer below input, 1 padding below indicator
        width: inner_w,
        height: 1,
    };

    let input_inner_width = text_width;
    let total_visual_lines = display_lines;

    let mut cursor_visual_row: u16 = 0;
    let mut cursor_col_in_logical: u16 = 0;
    for (i, c) in app.chat.input.chars().enumerate() {
        if i == app.chat.cursor {
            break;
        }
        if c == '\n' {
            cursor_visual_row += 1;
            cursor_col_in_logical = 0;
        } else {
            cursor_col_in_logical += 1;
            if cursor_col_in_logical.is_multiple_of(input_inner_width) {
                cursor_visual_row += 1;
            }
        }
    }
    let cursor_x = cursor_col_in_logical % input_inner_width;

    let max_visible = text_area.height;
    let mut input_scroll = app.chat.input_scroll;
    if cursor_visual_row < input_scroll {
        input_scroll = cursor_visual_row;
    } else if cursor_visual_row >= input_scroll + max_visible {
        input_scroll = cursor_visual_row + 1 - max_visible;
    }
    let max_scroll = total_visual_lines.saturating_sub(max_visible);
    input_scroll = input_scroll.min(max_scroll);

    let paragraph_content: Vec<Line> = if app.chat.input.is_empty() {
        let placeholder = "Ask anything... \"Fix a TODO in the codebase\"";
        let active_theme = crate::tui::render::get_active_theme(app);
        let placeholder_color = active_theme.text_muted;
        vec![Line::from(Span::styled(
            placeholder,
            Style::default().fg(placeholder_color),
        ))]
    } else {
        wrapped_lines.into_iter().map(Line::from).collect()
    };

    if app.ui.screen == Screen::AskUser {
        render_ask_user_in_input_box(frame, app, text_block_area);
    } else if app.ui.screen == Screen::Permissions {
        render_permissions_in_input_box(frame, app, text_block_area);
    } else {
        frame.render_widget(
            Paragraph::new(paragraph_content)
                .style(Style::default().fg(fg_color))
                .scroll((input_scroll, 0)),
            text_area,
        );

        let cursor_y_in_box = cursor_visual_row.saturating_sub(input_scroll);
        let target_y = text_area.y + cursor_y_in_box;
        let max_y = text_area.bottom().saturating_sub(1);

        if !app.chat.shell_focused
            && !app.ui.model_picker_open
            && target_y <= max_y
            && target_y >= text_area.y
        {
            frame.set_cursor_position((text_area.x + cursor_x, target_y));
        }

        let mode_color = match app.core.dev_mode {
            crate::app::DevelopMode::Plan => Color::Rgb(168, 85, 247), // purple
            crate::app::DevelopMode::Build => Color::Rgb(59, 130, 246), // blue (mockup)
        };

        let mode_str = app.dev_mode_label();
        let model_str = app.model_label();
        let mode_len = u16::try_from(mode_str.chars().count()).unwrap_or(u16::MAX);
        let model_len = u16::try_from(model_str.chars().count()).unwrap_or(u16::MAX);

        let mode_rect = Rect {
            x: mode_model_row_area.x,
            y: mode_model_row_area.y,
            width: mode_len,
            height: 1,
        };
        let model_rect = Rect {
            x: mode_model_row_area
                .x
                .saturating_add(mode_len)
                .saturating_add(3), // after mode label + separator " · "
            y: mode_model_row_area.y,
            width: model_len,
            height: 1,
        };
        app.chat.mode_area.set(Some(mode_rect));
        app.chat.model_area.set(Some(model_rect));

        let active_theme = crate::tui::render::get_active_theme(app);
        let separator_style = Style::default().fg(active_theme.text_muted);
        let mut mode_model_spans = vec![
            Span::styled(
                mode_str,
                Style::default().fg(mode_color).add_modifier(Modifier::BOLD),
            ),
            Span::styled(" · ", separator_style),
        ];

        for (idx, word) in model_str.split_whitespace().enumerate() {
            if idx > 0 {
                mode_model_spans.push(Span::raw(" "));
            }
            let style = if idx < 2 {
                Style::default().fg(active_theme.text)
            } else {
                Style::default().fg(active_theme.text_muted)
            };
            mode_model_spans.push(Span::styled(word.to_owned(), style));
        }

        frame.render_widget(
            Paragraph::new(Line::from(mode_model_spans)).alignment(Alignment::Left),
            mode_model_row_area,
        );
    } // end else (normal input render)

    if let Some(pane) = right_pane {
        render_todos(frame, app, pane);
    }

    render_statusbar(frame, app, statusbar_area);

    let has_modal = app.ui.show_trust_modal
        || app.ui.model_picker_open
        || app.ui.theme_picker_open
        || app.ui.agent_picker_open
        || app.ui.screen == Screen::Sessions
        || app.ui.screen == Screen::Setup
        || app
            .proc
            .pending
            .as_ref()
            .is_some_and(|p| matches!(p, crate::app::PendingTask::ConfirmFunction { .. }));

    if has_modal {
        dim_buffer(frame, area);
    }

    if app.ui.show_trust_modal {
        crate::tui::render::trust_modal::render_trust_modal(frame, app);
    }

    if app.ui.model_picker_open {
        render_model_picker_modal(frame, app, area);
    }

    if app.ui.theme_picker_open {
        render_theme_picker_modal(frame, app, area);
    }

    if app.ui.agent_picker_open {
        render_agent_picker_modal(frame, app, area);
    }

    if app.ui.screen == Screen::Sessions {
        crate::tui::render::sessions::render_sessions_popup(frame, app, area);
    }

    if app.ui.screen == Screen::Setup {
        crate::tui::render::setup::render_setup_modal(frame, app, area);
    }

    if let Some(crate::app::PendingTask::ConfirmFunction { .. }) = &app.proc.pending {
        render_confirm_modal(frame, app, area);
    }
}
