use std::env;
use std::io::{Stdout, stdout};
use std::time::Duration;

use crossterm::{
    event::{self, Event, KeyCode, KeyEventKind},
    execute,
    terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
};
use ratatui::{Terminal, prelude::CrosstermBackend, widgets::TableState};
use sqlx::{PgPool, postgres::PgPoolOptions};

pub mod controls;
pub mod editor;
pub mod errors;
pub mod fields;
pub mod relations;
pub mod ui;

#[cfg(test)]
mod db_tests;

use editor::EditorKind;
use errors::PtError;
use fields::{Field, FieldMeta};
use relations::{Relation, RelationType};
use tui_dialog::Dialog;
use ui::{
    Pane,
    main_pane::{MainPane, Mode, Sort, SortOrder},
    sidebar::{self, RelationTypeFilter, SchemaFilter, Sidebar},
    tables,
};

/// How many records per page in the Data pane.
pub const RESULTS_PER_PAGE: usize = 25;

/// Data used in running the app.
//
// 'st = lifetime for Sidebar Table
// 'mt = lifetime for MainPane Table
pub struct App<'st, 'mt> {
    pub focused_pane: Pane,
    pub main_pane: MainPane<'mt>,
    pub sidebar: Sidebar<'st>, // has relations that may change depending on filters
    pub relations: Vec<Relation>, // will not change through course of app
    pub pool: PgPool,
    pub message_to_user: Vec<MessageToUser>,
    pub conn_info: String,
    pub conn_string: String,
    pub show_help: bool,
    pub exit: bool,
    pub action: Action,
}

/// All the actions the user can take.
#[non_exhaustive]
pub enum Action {
    // actions from anywhere
    Wait,
    Exit,
    ShowHelp,
    CloseHelp,
    SwitchPane,
    SwitchMainPaneMode,
    GoToNextRow,
    GoToPrevRow,
    GoToTopOrBottom,
    // actions in sidebar
    FilterRelationByType(RelationTypeFilter),
    OpenRelationNameDialog,
    FilterRelationByName(KeyCode),
    ClearRelationFilters,
    ToggleSchema,
    InsertRecord,
    // actions in data
    SortByColumn,
    OpenDataWhereDialog,
    FilterDataByWhere(KeyCode),
    ClearResults,
    ViewRecord,
    EditRecord,
    OpenDeleteRecordDialog,
    ConfirmDeleteRecord(KeyCode),
    GoToNextPage,
    GoToPrevPage,
    GoToNextCol,
    GoToPrevCol,
    // actions in data definition
    ScrollDown,
    ScrollUp,
    ScrollLeft,
    ScrollRight,
}

impl App<'_, '_> {
    /// Get the current relation selected in the sidebar.
    pub fn current_relation(&self) -> Option<Relation> {
        match self.sidebar.table_state.selected() {
            Some(v) => self.sidebar.current_relations.get(v).cloned(),
            None => None,
        }
    }

    /// A wrapper around `Fields::collect()`, so that fn is more testable.
    pub async fn collect_fields(&mut self) -> Result<(), PtError> {
        if let Some(v) = self.current_relation() {
            // Take any submitted input from filter dialog and make it a where clause for queries.
            let where_clause = if self.main_pane.filter_dialog.submitted_input.is_empty() {
                None
            } else {
                Some(self.main_pane.filter_dialog.submitted_input.clone())
            };

            let result_set = Field::collect(
                &self.pool,
                &v,
                where_clause,
                &self.main_pane.sort,
                self.main_pane.page,
            )
            .await?;
            self.main_pane.count = result_set.total_count as usize;
            self.main_pane.num_pages =
                (self.main_pane.count as f64 / RESULTS_PER_PAGE as f64).ceil() as usize;
            self.main_pane.data = result_set.current_set;
            self.main_pane.table_state.select_first();
            Ok(())
        } else {
            Err(PtError::NoCurrentRelation)
        }
    }

    /// Create a Table from the current relations.
    pub fn make_relation_table(&mut self) {
        // Start with all relations gathered when app started.
        self.sidebar.current_relations = self.relations.clone();

        // Filter relations available according to various filters set by user.
        // Filter by relation type.
        self.sidebar
            .current_relations
            .retain(|x| match self.sidebar.relation_type_filter {
                RelationTypeFilter::All => true,
                RelationTypeFilter::Table => x.relation_type == RelationType::Table,
                RelationTypeFilter::Sequence => x.relation_type == RelationType::Sequence,
                RelationTypeFilter::View => x.relation_type == RelationType::View,
            });

        // Filter by schema.
        self.sidebar
            .current_relations
            .retain(|x| match self.sidebar.schema_filter {
                SchemaFilter::All => true,
                SchemaFilter::Public => x.schema == "public",
            });

        // Filter by name.
        if self.sidebar.relation_name_dialog.submitted {
            self.sidebar.current_relations.retain(|x| {
                x.name
                    .contains(&self.sidebar.relation_name_dialog.submitted_input)
            });
        }

        // Create the table.
        self.sidebar.table = tables::make_relation_table(&self.sidebar.current_relations);

        // Select appropriate row.
        if self.sidebar.table.is_some() {
            self.sidebar.table_state.select_first();
        } else {
            self.sidebar.table_state = TableState::new().with_selected(None);
        }
    }

    /// Create a Table from a relation's data.
    pub fn make_data_table(&mut self) {
        self.main_pane.table = tables::make_data_table(&self.main_pane.data);
    }
}

/// Message to the user, displayed in status/help area, after certain actions.
#[derive(Clone)]
pub struct MessageToUser {
    pub message: String,
    pub level: MessageLevel,
}

impl MessageToUser {
    fn new(message: String, level: MessageLevel) -> Self {
        Self { message, level }
    }
}

/// The kind of message being given to the user.
#[derive(Clone)]
pub enum MessageLevel {
    Notice,
    Error,
}

#[tokio::main]
async fn main() {
    println!("Starting app...");
    dotenvy::dotenv().expect("Unable to load .env file");
    let conn_string = env::var("DATABASE_URL").unwrap();

    let pool = PgPoolOptions::new()
        .max_connections(1)
        .connect(&conn_string)
        .await
        .expect("Unable to connect to the database");

    println!(
        "Getting all non-system relations and their definitions. This may take a few moments..."
    );

    // Get all relations.
    let mut relations = relations::psql_d_non_system(&pool).await.unwrap();

    // Since getting the data definitions of each relation takes some time, do it now for all of
    // them rather than when navigating through each one after app has started. It makes startup
    // take slightly longer, but it's less annoying than having to wait a bit the first time you
    // navigate through the list of relations in the sidebar.
    for relation in relations.iter_mut() {
        relation.data_definition = relations::get_data_definition(relation, &conn_string);
    }
    let selected_row = if relations.is_empty() { None } else { Some(0) };

    // Create App.
    let mut app = App {
        focused_pane: Pane::Sidebar,
        sidebar: Sidebar {
            current_relations: relations.clone(),
            table: None,
            table_state: TableState::new(),
            relation_type_filter: RelationTypeFilter::All,
            schema_filter: SchemaFilter::All,
            relation_name_dialog: Dialog::default(),
        },
        main_pane: MainPane {
            mode: Mode::DataDefinition,
            data: vec![],
            table: None,
            table_state: TableState::new().with_selected(selected_row),
            page: 0,
            count: 0,
            num_pages: 0,
            horizontal_scroll: 0,
            horizontal_max: 0,
            vertical_scroll: 0,
            vertical_max: 0,
            sort: Sort::default(),
            delete_dialog: Dialog::default(),
            filter_dialog: Dialog::default(),
        },
        relations,
        pool,
        message_to_user: vec![],
        conn_info: "".to_string(),
        conn_string,
        show_help: false,
        exit: false,
        action: Action::Wait,
    };

    app.make_relation_table();

    // Initialize the terminal.
    let mut terminal = match Terminal::new(CrosstermBackend::new(stdout())) {
        Ok(v) => v,
        Err(e) => {
            eprintln!("Error creating new terminal interface: {e}");
            return;
        }
    };

    if let Err(e) = execute!(terminal.backend_mut(), EnterAlternateScreen) {
        eprintln!("Error switching to alternate screen: {e}");
        return;
    }

    if let Err(e) = enable_raw_mode() {
        eprintln!("Error entering raw mode: {e}");
        return;
    }

    // Run the app.
    if let Err(e) = run(&mut app, &mut terminal).await {
        eprintln!("Error running app: {e}");
        return;
    }

    // Restore the terminal to its original state, then exit.
    if let Err(e) = disable_raw_mode() {
        eprintln!("Error disabling raw mode: {e}");
        return;
    }

    if let Err(e) = execute!(terminal.backend_mut(), LeaveAlternateScreen) {
        eprintln!("Error leaving alternate screen: {e}");
    }
}

/// Run the application's main loop.
///
/// This handles all errors from the program's own code, so that a useful error message can be
/// returned to the user/developer.
async fn run<'st, 'mt>(
    app: &mut App<'st, 'mt>,
    terminal: &mut Terminal<CrosstermBackend<Stdout>>,
) -> Result<(), PtError> {
    while !app.exit {
        terminal.draw(|frame| ui::render(frame, app))?;
        // Watch for and handle user input.
        app.action = match event::read()? {
            // NOTE: it's important to check that the event is a key press event as
            // crossterm also emits key release and repeat events on Windows.
            Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
                // Clear any previous message to the user.
                app.message_to_user = vec![];

                // Set action from key press.
                if app.main_pane.delete_dialog.open {
                    Action::ConfirmDeleteRecord(key_event.code)
                } else if app.main_pane.filter_dialog.open {
                    Action::FilterDataByWhere(key_event.code)
                } else if app.sidebar.relation_name_dialog.open {
                    Action::FilterRelationByName(key_event.code)
                } else {
                    match key_event.code {
                        KeyCode::Char('q') => Action::Exit,
                        KeyCode::Char('?') => Action::ShowHelp,
                        KeyCode::Tab => Action::SwitchPane,
                        KeyCode::Char('d') => Action::SwitchMainPaneMode,
                        // Everything else is by pane.
                        _ => match app.focused_pane {
                            Pane::Sidebar => match key_event.code {
                                KeyCode::Char('r') => {
                                    Action::FilterRelationByType(RelationTypeFilter::All)
                                }
                                KeyCode::Char('t') => {
                                    Action::FilterRelationByType(RelationTypeFilter::Table)
                                }
                                KeyCode::Char('s') => {
                                    Action::FilterRelationByType(RelationTypeFilter::Sequence)
                                }
                                KeyCode::Char('v') => {
                                    Action::FilterRelationByType(RelationTypeFilter::View)
                                }
                                KeyCode::Char('/') => Action::OpenRelationNameDialog,
                                KeyCode::Char('n') => Action::ToggleSchema,
                                KeyCode::Char('i') => Action::InsertRecord,
                                KeyCode::Char('j') | KeyCode::Down => Action::GoToNextRow,
                                KeyCode::Char('k') | KeyCode::Up => Action::GoToPrevRow,
                                KeyCode::Char('g') => Action::GoToTopOrBottom,
                                KeyCode::Esc => {
                                    if app.show_help {
                                        Action::CloseHelp
                                    } else {
                                        Action::ClearRelationFilters
                                    }
                                }
                                _ => Action::Wait,
                            },
                            // Handle according to which mode app is in.
                            Pane::Main => match app.main_pane.mode {
                                Mode::Data => match key_event.code {
                                    KeyCode::Char('j') | KeyCode::Down => Action::GoToNextRow,
                                    KeyCode::Char('k') | KeyCode::Up => Action::GoToPrevRow,
                                    KeyCode::Char('n') | KeyCode::PageDown => Action::GoToNextPage,
                                    KeyCode::Char('p') | KeyCode::PageUp => Action::GoToPrevPage,
                                    KeyCode::Char('h') | KeyCode::Left => Action::GoToPrevCol,
                                    KeyCode::Char('l') | KeyCode::Right => Action::GoToNextCol,
                                    KeyCode::Char('e') => Action::EditRecord,
                                    KeyCode::Char('v') => Action::ViewRecord,
                                    KeyCode::Delete => Action::OpenDeleteRecordDialog,
                                    KeyCode::Char('g') => Action::GoToTopOrBottom,
                                    KeyCode::Char('s') => Action::SortByColumn,
                                    KeyCode::Char('/') => Action::OpenDataWhereDialog,
                                    KeyCode::Esc => {
                                        if app.show_help {
                                            Action::CloseHelp
                                        } else {
                                            Action::ClearResults
                                        }
                                    }
                                    _ => Action::Wait,
                                },
                                Mode::DataDefinition => match key_event.code {
                                    KeyCode::Char('j') | KeyCode::Down => Action::ScrollDown,
                                    KeyCode::Char('k') | KeyCode::Up => Action::ScrollUp,
                                    KeyCode::Char('h') | KeyCode::Left => Action::ScrollLeft,
                                    KeyCode::Char('l') | KeyCode::Right => Action::ScrollRight,
                                    KeyCode::Esc => {
                                        if app.show_help {
                                            Action::CloseHelp
                                        } else {
                                            Action::Wait
                                        }
                                    }
                                    _ => Action::Wait,
                                },
                            },
                        },
                    }
                }
            }
            _ => Action::Wait,
        };

        // Do the user's requested action.
        match &app.action {
            Action::Exit => app.exit = true,
            Action::CloseHelp => app.show_help = false,
            Action::ShowHelp => app.show_help = !app.show_help,
            Action::SwitchPane => {
                // Reset Sort and Filter dialog.
                app.main_pane.sort = Sort::default();
                app.main_pane.filter_dialog = Dialog::default();
                // Switch focused pane.
                app.focused_pane = match app.focused_pane {
                    Pane::Sidebar => Pane::Main,
                    Pane::Main => Pane::Sidebar,
                }
            }
            Action::SwitchMainPaneMode => {
                app.main_pane.mode = match app.main_pane.mode {
                    Mode::DataDefinition => {
                        match app.collect_fields().await {
                            Ok(()) => app.make_data_table(),
                            Err(e) => {
                                app.message_to_user
                                    .push(MessageToUser::new(e.to_string(), MessageLevel::Error));
                                app.main_pane.data = vec![];
                                app.main_pane.table = None;
                            }
                        }
                        Mode::Data
                    }
                    Mode::Data => Mode::DataDefinition,
                }
            }
            Action::OpenRelationNameDialog => app.sidebar.relation_name_dialog.open = true,
            Action::FilterRelationByType(r_type) => {
                app.sidebar.relation_type_filter = *r_type;
                app.make_relation_table();
                match app.collect_fields().await {
                    Ok(()) => app.make_data_table(),
                    Err(e) => {
                        app.message_to_user
                            .push(MessageToUser::new(e.to_string(), MessageLevel::Error));
                        app.main_pane.data = vec![];
                        app.main_pane.table = None;
                    }
                }
            }
            Action::FilterRelationByName(key_code) => {
                app.sidebar.relation_name_dialog.key_action(key_code);
                if app.sidebar.relation_name_dialog.submitted {
                    app.make_relation_table();
                    match app.collect_fields().await {
                        Ok(()) => app.make_data_table(),
                        Err(e) => {
                            app.message_to_user
                                .push(MessageToUser::new(e.to_string(), MessageLevel::Error));
                            app.main_pane.data = vec![];
                            app.main_pane.table = None;
                        }
                    }
                }
            }
            Action::ClearRelationFilters => {
                app.sidebar.relation_name_dialog.submitted_input.clear();
                app.sidebar.relation_type_filter = RelationTypeFilter::All;
                app.make_relation_table();
                match app.collect_fields().await {
                    Ok(()) => app.make_data_table(),
                    Err(e) => {
                        app.message_to_user
                            .push(MessageToUser::new(e.to_string(), MessageLevel::Error));
                        app.main_pane.data = vec![];
                        app.main_pane.table = None;
                    }
                }
            }
            Action::ToggleSchema => {
                app.sidebar.schema_filter = match app.sidebar.schema_filter {
                    SchemaFilter::All => SchemaFilter::Public,
                    SchemaFilter::Public => SchemaFilter::All,
                };
                app.make_relation_table();
                match app.collect_fields().await {
                    Ok(()) => app.make_data_table(),
                    Err(e) => {
                        app.message_to_user
                            .push(MessageToUser::new(e.to_string(), MessageLevel::Error));
                        app.main_pane.data = vec![];
                        app.main_pane.table = None;
                    }
                }
            }
            Action::GoToNextRow => match app.focused_pane {
                Pane::Sidebar => {
                    /* NOTE: It's necessary to check, before doing anything, that the selected
                       row is not the last one, as TableState doesn't actually know how many rows
                       are in the table. Even though it will correct itself if it goes too far (and
                       so there is no visual clue this happens), we don't want to do any of the
                       logic that follows from trying to select the next row. Otherwise, for
                       instance, if the user had the app set to show the data in the main pane and
                       held down j or down arrow, the app would continually run the query to
                       collect the fields for that relation and create the table. This is not
                       documented anywhere that I can tell - found it in their Discord chat after
                       noticing odd behavior.
                    */
                    if app.sidebar.table.is_some()
                        && let Some(v) = app.sidebar.table_state.selected()
                        && app.sidebar.current_relations.len() - 1 > v
                    {
                        app.sidebar.table_state.select_next();
                        match app.main_pane.mode {
                            Mode::DataDefinition => {
                                app.main_pane.horizontal_scroll = 0;
                                app.main_pane.vertical_scroll = 0;
                            }
                            Mode::Data => {
                                app.main_pane.page = 0;
                                app.main_pane.table_state.select_column(None);
                                match app.collect_fields().await {
                                    Ok(()) => app.make_data_table(),
                                    Err(e) => {
                                        app.message_to_user.push(MessageToUser::new(
                                            e.to_string(),
                                            MessageLevel::Error,
                                        ));
                                        app.main_pane.data = vec![];
                                        app.main_pane.table = None;
                                    }
                                }
                            }
                        }
                    }
                }
                Pane::Main => {
                    if app.main_pane.table_state.selected().is_some() {
                        app.main_pane.table_state.select_next();
                    }
                }
            },
            Action::GoToPrevRow => match app.focused_pane {
                Pane::Sidebar => {
                    // NOTE: See comment above on GoToNextRow for similar reasoning on the
                    // check of selected() index.
                    if app.sidebar.table.is_some()
                        && let Some(v) = app.sidebar.table_state.selected()
                        && v != 0
                    {
                        app.sidebar.table_state.select_previous();
                        // Reset view on Main Pane.
                        match app.main_pane.mode {
                            Mode::DataDefinition => {
                                app.main_pane.horizontal_scroll = 0;
                                app.main_pane.vertical_scroll = 0;
                            }
                            Mode::Data => {
                                app.main_pane.page = 0;
                                app.main_pane.table_state.select_column(None);
                                match app.collect_fields().await {
                                    Ok(()) => app.make_data_table(),
                                    Err(e) => {
                                        app.message_to_user.push(MessageToUser::new(
                                            e.to_string(),
                                            MessageLevel::Error,
                                        ));
                                        app.main_pane.data = vec![];
                                        app.main_pane.table = None;
                                    }
                                }
                            }
                        }
                    }
                }
                Pane::Main => {
                    if app.main_pane.table_state.selected().is_some() {
                        app.main_pane.table_state.select_previous();
                    }
                }
            },
            Action::GoToTopOrBottom => match app.focused_pane {
                Pane::Sidebar => {
                    if let Some(_) = app.sidebar.table_state.selected()
                        && app.sidebar.table.is_some()
                    {
                        // Wait for another key press within time period, handle.
                        if event::poll(Duration::from_millis(750))? {
                            match event::read()? {
                                Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
                                    match key_event.code {
                                        KeyCode::Char('g') => {
                                            app.sidebar.table_state.select_first();
                                        }
                                        KeyCode::Char('e') => {
                                            app.sidebar.table_state.select_last();
                                        }
                                        _ => (),
                                    }
                                }
                                _ => (),
                            }
                        }
                    } else {
                        app.message_to_user.push(MessageToUser::new(
                            "No relations.".to_string(),
                            MessageLevel::Error,
                        ));
                    }
                }
                Pane::Main => {
                    if let Some(_) = app.main_pane.table_state.selected()
                        && !app.main_pane.data.is_empty()
                    {
                        // Wait for another key press within time period, handle.
                        if event::poll(Duration::from_millis(750))? {
                            match event::read()? {
                                Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
                                    match key_event.code {
                                        KeyCode::Char('g') => {
                                            app.main_pane.table_state.select_first();
                                        }
                                        KeyCode::Char('e') => {
                                            app.main_pane.table_state.select_last();
                                        }
                                        KeyCode::Char('h') => {
                                            app.main_pane.page = 0;
                                            match app.collect_fields().await {
                                                Ok(()) => app.make_data_table(),
                                                Err(e) => {
                                                    app.message_to_user.push(MessageToUser::new(
                                                        e.to_string(),
                                                        MessageLevel::Error,
                                                    ));
                                                    app.main_pane.data = vec![];
                                                    app.main_pane.table = None;
                                                }
                                            }
                                        }
                                        KeyCode::Char('l') => {
                                            app.main_pane.page = app.main_pane.num_pages - 1;
                                            match app.collect_fields().await {
                                                Ok(()) => app.make_data_table(),
                                                Err(e) => {
                                                    app.message_to_user.push(MessageToUser::new(
                                                        e.to_string(),
                                                        MessageLevel::Error,
                                                    ));
                                                    app.main_pane.data = vec![];
                                                    app.main_pane.table = None;
                                                }
                                            }
                                        }
                                        _ => (),
                                    }
                                }
                                _ => (),
                            }
                        }
                    } else {
                        app.message_to_user.push(MessageToUser::new(
                            "No records to go to.".to_string(),
                            MessageLevel::Error,
                        ));
                    }
                }
            },
            Action::InsertRecord => {
                if app.sidebar.table_state.selected().is_some() {
                    let relation = app.current_relation().unwrap();
                    let mut fields;

                    if app.main_pane.data.is_empty() {
                        let field_meta = match FieldMeta::collect(&app.pool, &relation).await {
                            Ok(v) => v,
                            Err(e) => {
                                app.message_to_user.push(MessageToUser::new(
                                    format!("Error getting metadata: {e}."),
                                    MessageLevel::Error,
                                ));
                                continue;
                            }
                        };

                        // Convert from FieldMeta to Field.
                        fields = field_meta
                            .into_iter()
                            .map(Field::from)
                            .collect::<Vec<Field>>();
                    } else {
                        // Use the first row in the existing relation for the fields, but first
                        // clear out any data in them since this is a new insert.
                        fields = app.main_pane.data[0].clone();
                        for field in fields.iter_mut() {
                            field.toml_value = None;
                        }
                    }

                    // Because `editor` calls another terminal program,
                    // leave and enter alternate screen around it to ensure that
                    // the screen gets cleared properly.
                    execute!(terminal.backend_mut(), LeaveAlternateScreen)?;
                    let fields = match editor::editor(fields, &relation.name, &EditorKind::Insert) {
                        Ok(v) => v,
                        Err(PtError::Abandoned) => {
                            app.message_to_user.push(MessageToUser::new(
                                "Insert abandoned.".to_string(),
                                MessageLevel::Error,
                            ));
                            execute!(terminal.backend_mut(), EnterAlternateScreen)?;
                            // Force redraw of everything.
                            terminal.clear()?;
                            continue;
                        }
                        Err(e) => {
                            app.message_to_user.push(MessageToUser::new(
                                format!("Error inserting record: {e}"),
                                MessageLevel::Error,
                            ));
                            execute!(terminal.backend_mut(), EnterAlternateScreen)?;
                            // Force redraw of everything.
                            terminal.clear()?;
                            continue;
                        }
                    };
                    execute!(terminal.backend_mut(), EnterAlternateScreen)?;
                    // Force redraw of everything.
                    terminal.clear()?;

                    match editor::insert(fields, relation.clone(), &app.pool).await {
                        Ok(_) => {
                            app.message_to_user.push(MessageToUser::new(
                                "Insert successful.".to_string(),
                                MessageLevel::Notice,
                            ));

                            // Refresh the data so changes are shown.
                            match app.collect_fields().await {
                                Ok(()) => app.make_data_table(),
                                Err(e) => {
                                    app.message_to_user.push(MessageToUser::new(
                                        e.to_string(),
                                        MessageLevel::Error,
                                    ));
                                    app.main_pane.data = vec![];
                                    app.main_pane.table = None;
                                }
                            }
                        }
                        Err(e) => app.message_to_user.push(MessageToUser::new(
                            format!("Error inserting record: {e}"),
                            MessageLevel::Error,
                        )),
                    }

                    // app.message_to_user.push(message);
                } else {
                    app.message_to_user.push(MessageToUser::new(
                        "No relation to insert into.".to_string(),
                        MessageLevel::Error,
                    ));
                }
            }
            Action::EditRecord => {
                if let Some(row_index) = app.main_pane.table_state.selected()
                    && !app.main_pane.data.is_empty()
                    && let Some(relation) = app.current_relation()
                {
                    let primary_key = match relation.get_primary_key_column(&app.pool).await {
                        Ok(Some(v)) => v,
                        Ok(None) => {
                            app.message_to_user.push(MessageToUser::new(
                                "Unable to edit record - no primary key.".to_string(),
                                MessageLevel::Error,
                            ));
                            continue;
                        }
                        Err(e) => {
                            app.message_to_user.push(MessageToUser::new(
                                format!("Database error: {e}"),
                                MessageLevel::Error,
                            ));
                            continue;
                        }
                    };

                    let mut fields = app.main_pane.data[row_index].clone();

                    for field in fields.iter_mut() {
                        if field.name == primary_key {
                            field.is_primary_key = Some(true);
                        }
                    }

                    // Because `editor` calls another terminal program,
                    // leave and enter alternate screen around it to ensure that
                    // the screen gets cleared properly.
                    execute!(terminal.backend_mut(), LeaveAlternateScreen)?;

                    let fields = match editor::editor(fields, &relation.name, &EditorKind::Update) {
                        Ok(v) => v,
                        Err(PtError::Abandoned) => {
                            app.message_to_user.push(MessageToUser::new(
                                "Update abandoned.".to_string(),
                                MessageLevel::Error,
                            ));
                            execute!(terminal.backend_mut(), EnterAlternateScreen)?;
                            // Force redraw of everything.
                            terminal.clear()?;
                            continue;
                        }
                        Err(e) => {
                            app.message_to_user.push(MessageToUser::new(
                                format!("Error editing: {e}"),
                                MessageLevel::Error,
                            ));
                            execute!(terminal.backend_mut(), EnterAlternateScreen)?;
                            // Force redraw of everything.
                            terminal.clear()?;
                            continue;
                        }
                    };

                    execute!(terminal.backend_mut(), EnterAlternateScreen)?;
                    // Force redraw of everything.
                    terminal.clear()?;

                    match editor::update(fields, relation.clone(), &app.pool).await {
                        Ok(_) => {
                            app.message_to_user.push(MessageToUser::new(
                                "Edit successful.".to_string(),
                                MessageLevel::Notice,
                            ));
                            // Refresh the data so changes are shown.
                            match app.collect_fields().await {
                                Ok(()) => app.make_data_table(),
                                Err(e) => {
                                    app.message_to_user.push(MessageToUser::new(
                                        e.to_string(),
                                        MessageLevel::Error,
                                    ));
                                    app.main_pane.data = vec![];
                                    app.main_pane.table = None;
                                }
                            }
                        }
                        Err(e) => app
                            .message_to_user
                            .push(MessageToUser::new(e.to_string(), MessageLevel::Error)),
                    }
                } else {
                    app.message_to_user.push(MessageToUser::new(
                        "No record to edit.".to_string(),
                        MessageLevel::Error,
                    ));
                }
            }
            Action::ViewRecord => {
                if let Some(row_index) = app.main_pane.table_state.selected()
                    && !app.main_pane.data.is_empty()
                    && let Some(relation) = app.current_relation()
                {
                    let fields = app.main_pane.data[row_index].clone();

                    // Because `editor` calls another terminal program,
                    // leave and enter alternate screen around it to ensure that
                    // the screen gets cleared properly.
                    execute!(terminal.backend_mut(), LeaveAlternateScreen)?;

                    let _ = editor::editor(fields, &relation.name, &EditorKind::View);
                    execute!(terminal.backend_mut(), EnterAlternateScreen)?;
                    // Force redraw of everything.
                    terminal.clear()?;
                } else {
                    app.message_to_user.push(MessageToUser::new(
                        "No record to view.".to_string(),
                        MessageLevel::Error,
                    ));
                }
            }
            Action::OpenDeleteRecordDialog => {
                if app.main_pane.table_state.selected().is_some()
                    && !app.main_pane.data.is_empty()
                    && let Some(relation) = app.current_relation()
                {
                    // Make sure the relation has a primary key to be used to delete the record.
                    // If so, open dialog for confirmation.
                    match relation.get_primary_key_column(&app.pool).await {
                        Ok(Some(_)) => app.main_pane.delete_dialog.open = true,
                        Ok(None) => {
                            app.message_to_user.push(MessageToUser::new(
                                "Unable to edit record - no primary key.".to_string(),
                                MessageLevel::Error,
                            ));
                        }
                        Err(e) => {
                            app.message_to_user.push(MessageToUser::new(
                                format!("Database error: {e}"),
                                MessageLevel::Error,
                            ));
                        }
                    }
                } else {
                    app.message_to_user.push(MessageToUser::new(
                        "No record to delete.".to_string(),
                        MessageLevel::Error,
                    ));
                }
            }
            Action::ConfirmDeleteRecord(key_code) => {
                app.main_pane.delete_dialog.key_action(key_code);
                if app.main_pane.delete_dialog.submitted {
                    // Confirm user typed in confirmation correctly.
                    if app
                        .main_pane
                        .delete_dialog
                        .submitted_input
                        .to_lowercase()
                        .trim()
                        == "delete"
                    {
                        if let Some(v) = app.main_pane.table_state.selected()
                            && !app.main_pane.data.is_empty()
                            && let Some(relation) = app.current_relation()
                        {
                            let primary_key = match relation.get_primary_key_column(&app.pool).await
                            {
                                Ok(Some(v)) => v,
                                Ok(None) => {
                                    app.message_to_user.push(MessageToUser::new(
                                        "Unable to edit record - no primary key.".to_string(),
                                        MessageLevel::Error,
                                    ));
                                    continue;
                                }
                                Err(e) => {
                                    app.message_to_user.push(MessageToUser::new(
                                        format!("Database error: {e}"),
                                        MessageLevel::Error,
                                    ));
                                    continue;
                                }
                            };

                            // Get the record of selected row in table.
                            let field = match app.main_pane.data[v]
                                .iter()
                                .find(|x| x.name == primary_key)
                            {
                                Some(v) => v,
                                None => {
                                    app.message_to_user.push(MessageToUser::new(
                                        "Error getting selected row".to_string(),
                                        MessageLevel::Error,
                                    ));
                                    continue;
                                }
                            };

                            let query = format!(
                                "delete from {}.{} where {} = $1",
                                relation.schema, relation.name, primary_key,
                            );

                            let value = match field.toml_value.clone() {
                                Some(v) => v,
                                None => {
                                    app.message_to_user.push(MessageToUser::new(
                                        format!("Error getting value for {field:?}"),
                                        MessageLevel::Error,
                                    ));
                                    continue;
                                }
                            };

                            if value.is_integer() {
                                if let Err(e) = sqlx::query(&query)
                                    .bind(field.toml_value.clone().unwrap().as_integer())
                                    .execute(&app.pool)
                                    .await
                                {
                                    app.message_to_user.push(MessageToUser::new(
                                        format!("Database error: {e}"),
                                        MessageLevel::Error,
                                    ));
                                } else {
                                    app.message_to_user.push(MessageToUser::new(
                                        "Record deleted.".to_string(),
                                        MessageLevel::Notice,
                                    ));
                                    match app.collect_fields().await {
                                        Ok(()) => app.make_data_table(),
                                        Err(e) => {
                                            app.message_to_user.push(MessageToUser::new(
                                                e.to_string(),
                                                MessageLevel::Error,
                                            ));
                                            app.main_pane.data = vec![];
                                            app.main_pane.table = None;
                                        }
                                    }
                                }
                            } else if value.is_str() {
                                if let Err(e) = sqlx::query(&query)
                                    .bind(field.toml_value.clone().unwrap().as_str())
                                    .execute(&app.pool)
                                    .await
                                {
                                    app.message_to_user.push(MessageToUser::new(
                                        format!("Database error: {e}"),
                                        MessageLevel::Error,
                                    ));
                                } else {
                                    app.message_to_user.push(MessageToUser::new(
                                        "Record deleted.".to_string(),
                                        MessageLevel::Notice,
                                    ));
                                    match app.collect_fields().await {
                                        Ok(()) => app.make_data_table(),
                                        Err(e) => {
                                            app.message_to_user.push(MessageToUser::new(
                                                e.to_string(),
                                                MessageLevel::Error,
                                            ));
                                            app.main_pane.data = vec![];
                                            app.main_pane.table = None;
                                        }
                                    }
                                }
                            } else {
                                app.message_to_user.push(MessageToUser::new(
                                    "Unable to delete record - not yet implemented for the data type the primary key is.".to_string(),
                                    MessageLevel::Error,
                                ));
                            }
                        } else {
                            app.message_to_user.push(MessageToUser::new(
                                "No record to delete.".to_string(),
                                MessageLevel::Error,
                            ));
                        }
                    } else {
                        app.message_to_user.push(MessageToUser::new(
                            "Confirmation incorrect - deletion aborted.".to_string(),
                            MessageLevel::Notice,
                        ));
                    }
                }
            }
            Action::GoToNextPage => {
                if app.main_pane.page < app.main_pane.num_pages - 1 {
                    app.main_pane.page += 1;
                    match app.collect_fields().await {
                        Ok(()) => app.make_data_table(),
                        Err(e) => {
                            app.message_to_user
                                .push(MessageToUser::new(e.to_string(), MessageLevel::Error));
                            app.main_pane.data = vec![];
                            app.main_pane.table = None;
                        }
                    }
                }
            }
            Action::GoToPrevPage => {
                if app.main_pane.page != 0 {
                    app.main_pane.page -= 1;
                    match app.collect_fields().await {
                        Ok(()) => app.make_data_table(),
                        Err(e) => {
                            app.message_to_user
                                .push(MessageToUser::new(e.to_string(), MessageLevel::Error));
                            app.main_pane.data = vec![];
                            app.main_pane.table = None;
                        }
                    }
                }
            }
            Action::GoToNextCol => {
                app.main_pane.table_state.select_next_column();
            }
            Action::GoToPrevCol => {
                // Select previous col, except select none if already at start.
                match app.main_pane.table_state.selected_column() {
                    None => (),
                    Some(0) => app.main_pane.table_state.select_column(None),
                    _ => app.main_pane.table_state.select_previous_column(),
                }
            }
            Action::SortByColumn => {
                if let Some(record) = app.main_pane.data.first()
                    && let Some(field) =
                        record.get(app.main_pane.table_state.selected_column().unwrap_or(0))
                    && let Some(relation) = app.current_relation()
                {
                    app.main_pane.sort = Sort {
                        relation: Some(relation.clone()),
                        field: Some(field.name.clone()),
                        order: if app.main_pane.sort.relation == Some(relation)
                            && app.main_pane.sort.field == Some(field.name.clone())
                        {
                            match app.main_pane.sort.order {
                                SortOrder::Ascending => SortOrder::Descending,
                                SortOrder::Descending => SortOrder::Ascending,
                            }
                        } else {
                            SortOrder::default()
                        },
                    };
                    app.message_to_user.push(MessageToUser::new(
                        format!(
                            "Sorting data by {:?} in {} order. Press 's' again to reverse order.",
                            field.name,
                            format!("{:?}", app.main_pane.sort.order).to_lowercase()
                        ),
                        MessageLevel::Notice,
                    ));
                    match app.collect_fields().await {
                        Ok(()) => app.make_data_table(),
                        Err(e) => {
                            app.message_to_user
                                .push(MessageToUser::new(e.to_string(), MessageLevel::Error));
                            app.main_pane.data = vec![];
                            app.main_pane.table = None;
                        }
                    }
                } else {
                    app.message_to_user.push(MessageToUser::new(
                        "No records to sort.".to_string(),
                        MessageLevel::Error,
                    ));
                }
            }
            Action::OpenDataWhereDialog => {
                app.main_pane.filter_dialog.open = true;
            }
            Action::FilterDataByWhere(key_code) => {
                app.main_pane.filter_dialog.key_action(key_code);
                if app.main_pane.filter_dialog.submitted {
                    // Reset to first page of (possible) results.
                    app.main_pane.page = 0;

                    match app.collect_fields().await {
                        Ok(()) => app.make_data_table(),
                        Err(e) => {
                            app.message_to_user
                                .push(MessageToUser::new(e.to_string(), MessageLevel::Error));
                            app.main_pane.data = vec![];
                            app.main_pane.table = None;
                        }
                    }
                }
            }
            Action::ClearResults => {
                app.main_pane.filter_dialog.submitted_input.clear();
                app.main_pane.page = 0;
                match app.collect_fields().await {
                    Ok(()) => app.make_data_table(),
                    Err(e) => {
                        app.message_to_user
                            .push(MessageToUser::new(e.to_string(), MessageLevel::Error));
                        app.main_pane.data = vec![];
                        app.main_pane.table = None;
                    }
                }
            }
            Action::ScrollDown => {
                if app.main_pane.vertical_scroll < app.main_pane.vertical_max {
                    app.main_pane.vertical_scroll += 1;
                }
            }
            Action::ScrollUp => {
                if app.main_pane.vertical_scroll != 0 {
                    app.main_pane.vertical_scroll -= 1;
                }
            }
            Action::ScrollLeft => {
                if app.main_pane.horizontal_scroll != 0 {
                    app.main_pane.horizontal_scroll -= 1;
                }
            }
            Action::ScrollRight => {
                if app.main_pane.horizontal_scroll < app.main_pane.horizontal_max {
                    app.main_pane.horizontal_scroll += 1;
                }
            }
            Action::Wait => (),
        }
    }
    Ok(())
}
