//! Defines and renders the user interface.

use ratatui::{
    Frame,
    layout::{Constraint, Layout, Margin, Rect},
    style::{Color, Style},
    text::{Line, Span, Text},
    widgets::{
        Block, BorderType, Borders, Clear, Paragraph, Scrollbar, ScrollbarOrientation,
        ScrollbarState, Wrap,
    },
};
use tui_dialog::centered_rect;

pub mod main_pane;
pub mod sidebar;
pub mod tables;

use crate::{
    App, MessageLevel, controls,
    sidebar::SIDEBAR_SIZE,
    ui::{main_pane::Mode, sidebar::RelationTypeFilter},
};

/// The various panes that can be interacted with.
pub enum Pane {
    Sidebar,
    Main,
}

/// Render the UI.
pub fn render<'f, 'st, 'mt>(frame: &mut Frame<'f>, app: &mut App<'st, 'mt>) {
    /*
        Layout the areas and blocks of the UI.
    */
    let horizontal_layout =
        Layout::horizontal([Constraint::Length(SIDEBAR_SIZE), Constraint::Fill(1)])
            .split(frame.area());
    let left_layout =
        Layout::vertical([Constraint::Fill(1), Constraint::Length(10)]).split(horizontal_layout[0]);
    let main_area = horizontal_layout[1];
    let sidebar_area = left_layout[0];
    let bottom_area = left_layout[1];

    let mut sidebar_block = Block::default()
        .borders(Borders::ALL)
        .border_type(BorderType::Plain);

    // Title sidebar.
    let mut title_top = match app.sidebar.relation_type_filter {
        RelationTypeFilter::All => "Relations: all",
        RelationTypeFilter::Table => "Relations: tables",
        RelationTypeFilter::View => "Relations: views",
        RelationTypeFilter::Sequence => "Relations: sequences",
    }
    .to_string();

    if !app.sidebar.relation_name_dialog.submitted_input.is_empty() {
        title_top = format!(
            "{title_top} (name: {})",
            app.sidebar.relation_name_dialog.submitted_input
        );
    }
    sidebar_block = sidebar_block.title_top(title_top);

    let mut main_block = Block::default()
        .borders(Borders::ALL)
        .border_type(BorderType::Plain);

    let bottom_block = Block::default()
        .title_top(Line::from("Status/help").left_aligned())
        .title_bottom(Line::from(format!("pgtui v{}", std::env!("CARGO_PKG_VERSION"))).centered())
        .borders(Borders::ALL)
        .border_type(BorderType::Plain);

    // Style the focused pane to show it's focused.
    match app.focused_pane {
        Pane::Sidebar => {
            sidebar_block = sidebar_block
                .border_style(Color::Yellow)
                .border_type(BorderType::Thick)
                .title_style(Style::new().bold());
        }
        Pane::Main => {
            main_block = main_block
                .border_style(Color::Yellow)
                .border_type(BorderType::Thick)
                .title_style(Style::new().bold());
        }
    }

    // Give right pane title based on mode.
    match app.main_pane.mode {
        Mode::DataDefinition => {
            main_block = main_block.title_top(Line::from("Data Definition"));
        }
        Mode::Data => {
            main_block = main_block.title_top(Line::from("Data"));
        }
    }

    /*
        Create the widgets, wrapped in blocks, and render them in areas.
    */

    if let Some(v) = &app.sidebar.table {
        frame.render_stateful_widget(
            v.clone().block(sidebar_block),
            sidebar_area,
            &mut app.sidebar.table_state,
        );
    } else {
        frame.render_widget(
            Paragraph::new("No relations to display.").block(sidebar_block),
            sidebar_area,
        );
    }

    match &app.main_pane.mode {
        Mode::Data => {
            if let Some(v) = &app.main_pane.table {
                frame.render_stateful_widget(
                    v.clone().block(main_block),
                    main_area,
                    &mut app.main_pane.table_state,
                );
                let dialog_area = centered_rect(frame.area(), 45, 5, 0, 0);
                frame.render_widget(
                    app.main_pane
                        .delete_dialog
                        .title_top("Type 'delete' to confirm deletion of record.")
                        .clone(),
                    dialog_area,
                );
            } else {
                frame.render_widget(
                    Paragraph::new("No data to display.").block(main_block),
                    main_area,
                );
            }
            let dialog_area = centered_rect(frame.area(), 75, 5, 0, 0);
            frame.render_widget(
                app.main_pane
                    .filter_dialog
                    .title_top("Enter a WHERE clause to filter data by:")
                    .clone(),
                dialog_area,
            );
        }
        Mode::DataDefinition => {
            if let Some(v) = app.current_relation() {
                // Set the max length of the content that's horizontally scrollable,
                // accounting for size of area.
                let mut horizontal_count = 0;
                let mut vertical_count = 0;
                for line in v.clone().data_definition.unwrap().clone().lines() {
                    vertical_count += 1;
                    let current_count = line.trim_end().chars().count();
                    if current_count > horizontal_count {
                        horizontal_count = current_count;
                    }
                }
                app.main_pane.horizontal_max = if main_area.width < horizontal_count as u16 {
                    (horizontal_count as u16 - main_area.width) + 4
                } else {
                    0
                };
                app.main_pane.vertical_max = if main_area.height < vertical_count as u16 {
                    (vertical_count as u16 - main_area.height) + 4
                } else {
                    0
                };

                let data_definition = Paragraph::new(v.data_definition.unwrap().clone())
                    .scroll((
                        app.main_pane.vertical_scroll,
                        app.main_pane.horizontal_scroll,
                    ))
                    .block(main_block);

                let horizontal_scrollbar = Scrollbar::new(ScrollbarOrientation::HorizontalBottom)
                    .begin_symbol(Some("←"))
                    .end_symbol(Some("→"));

                let mut horizontal_scrollbar_state =
                    ScrollbarState::new(app.main_pane.horizontal_max as usize)
                        .position(app.main_pane.horizontal_scroll.into());

                let vertical_scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
                    .begin_symbol(Some("↑"))
                    .end_symbol(Some("↓"));

                let mut vertical_scrollbar_state =
                    ScrollbarState::new(app.main_pane.vertical_max as usize)
                        .position(app.main_pane.vertical_scroll.into());

                frame.render_widget(data_definition, main_area);
                frame.render_stateful_widget(
                    horizontal_scrollbar,
                    main_area.inner(Margin {
                        // using an inner vertical margin of 1 unit makes the scrollbar inside the block
                        vertical: 1,
                        horizontal: 1,
                    }),
                    &mut horizontal_scrollbar_state,
                );
                frame.render_stateful_widget(
                    vertical_scrollbar,
                    main_area.inner(Margin {
                        // using an inner vertical margin of 1 unit makes the scrollbar inside the block
                        vertical: 1,
                        horizontal: 1,
                    }),
                    &mut vertical_scrollbar_state,
                );
            } else {
                frame.render_widget(
                    Paragraph::new("No data definition to display.").block(main_block),
                    main_area,
                );
            }
        }
    }

    // Render the relation name dialog.
    let dialog_area = centered_rect(frame.area(), 75, 5, 0, 0);
    frame.render_widget(
        app.sidebar
            .relation_name_dialog
            .title_top("Enter relation name to filter by:")
            .clone(),
        dialog_area,
    );

    // Construct and then render the text in the help/status box.
    let mut help_status = Text::from("Press ? for keyboard shortcuts.");
    if !app.message_to_user.is_empty() {
        for msg in app.message_to_user.clone() {
            let style = match msg.level {
                MessageLevel::Notice => Style::default(),
                MessageLevel::Error => Style::default().red(),
            };
            help_status.push_line(Line::from(Span::from(msg.message).style(style)));
        }
    }
    let pages = if app.main_pane.mode == Mode::Data && app.main_pane.table.is_some() {
        let rows = if app.main_pane.count == 1 {
            "row"
        } else {
            "rows"
        };
        format!(
            "{} {}. Page {} of {}.",
            app.main_pane.count,
            rows,
            app.main_pane.page + 1,
            app.main_pane.num_pages
        )
    } else {
        "".to_string()
    };
    help_status.push_line(pages);
    frame.render_widget(
        Paragraph::new(help_status)
            .wrap(Wrap { trim: false })
            .block(bottom_block),
        bottom_area,
    );
    if app.show_help {
        let area = frame.area();
        let popup_area = Rect {
            x: 6,
            y: 3,
            width: area.width - 12,
            height: area.height - 6,
        };

        let popup_content = Paragraph::new(controls::CONTROLS).block(
            Block::new()
                .title("Keyboard Shortcuts")
                .title_bottom(Line::from(" Press Esc to Close ").centered())
                .borders(Borders::ALL)
                .border_type(BorderType::Plain),
        );
        frame.render_widget(Clear, popup_area);
        frame.render_widget(popup_content, popup_area);
    }
}
