//! ## SetupActivity
//!
//! `setup_activity` is the module which implements the Setup activity, which is the activity to
//! work on termscp configuration

// Locals
use std::env;

use tuirealm::ratatui::style::Color;
use tuirealm::state::{State, StateValue};
use tuirealm::terminal::TerminalAdapter;

use super::{Id, IdSsh, IdTheme, SetupActivity, ViewLayout};
// Ext
use crate::config::themes::Theme;

impl SetupActivity {
    /// On `ESC`, if there are changes in the configuration, the quit dialog
    /// must be shown; otherwise, we can exit without any problem.
    pub(super) fn action_on_esc(&mut self) {
        if self.config_changed() {
            self.mount_quit();
        } else {
            self.exit_reason = Some(super::ExitReason::Quit);
        }
    }

    /// Save all configurations. If current tab can load values, they will be loaded, otherwise they'll just be saved.
    /// Once all the configuration has been changed, set config_changed to false
    pub(super) fn action_save_all(&mut self) -> Result<(), String> {
        self.action_save_config()?;
        self.action_save_theme()?;
        // Set config changed to false
        self.set_config_changed(false);
        Ok(())
    }

    /// Save configuration
    fn action_save_config(&mut self) -> Result<(), String> {
        // Collect input values if in setup form
        if self.layout == ViewLayout::SetupForm {
            self.collect_input_values();
        }
        self.save_config()
    }

    /// Save configuration
    fn action_save_theme(&mut self) -> Result<(), String> {
        // Collect input values if in theme form
        if self.layout == ViewLayout::Theme {
            self.collect_styles()
                .map_err(|e| format!("'{e:?}' has an invalid color"))?;
        }
        // save theme
        self.save_theme()
    }

    /// Change view tab and load input values in order not to lose them
    pub(super) fn action_change_tab(&mut self, new_tab: ViewLayout) -> Result<(), String> {
        // load values for current tab first
        match self.layout {
            ViewLayout::SetupForm => self.collect_input_values(),
            ViewLayout::Theme => self
                .collect_styles()
                .map_err(|e| format!("'{e:?}' has an invalid color"))?,
            _ => {}
        }
        // Update view
        self.init(new_tab);
        Ok(())
    }

    /// Reset configuration input fields
    pub(super) fn action_reset_config(&mut self) -> Result<(), String> {
        match self.reset_config_changes() {
            Err(err) => Err(err),
            Ok(_) => {
                self.load_input_values();
                Ok(())
            }
        }
    }

    /// Reset configuration input fields
    pub(super) fn action_reset_theme(&mut self) -> Result<(), String> {
        match self.reset_theme_changes() {
            Err(err) => Err(err),
            Ok(_) => {
                self.load_styles();
                Ok(())
            }
        }
    }

    /// delete of a ssh key
    pub(super) fn action_delete_ssh_key(&mut self) {
        // Get key
        // get index
        let idx: Option<usize> = match self.app.state(&Id::Ssh(IdSsh::SshKeys)) {
            Ok(State::Single(StateValue::Usize(idx))) => Some(idx),
            _ => None,
        };
        // get ssh key and delete it
        if let Some(Err(err)) = idx
            .and_then(|i| self.config().iter_ssh_keys().nth(i).cloned())
            .and_then(|key| self.config().get_ssh_key(&key))
            .map(|(host, username, _)| self.delete_ssh_key(host.as_str(), username.as_str()))
        {
            // Report error
            self.mount_error(err.as_str());
        }
    }

    /// Create a new ssh key
    pub(super) fn action_new_ssh_key(&mut self) -> Result<(), String> {
        // get parameters
        let host: String = match self.app.state(&Id::Ssh(IdSsh::SshHost)) {
            Ok(State::Single(StateValue::String(host))) => host,
            _ => String::new(),
        };
        let username: String = match self.app.state(&Id::Ssh(IdSsh::SshUsername)) {
            Ok(State::Single(StateValue::String(user))) => user,
            _ => String::new(),
        };
        // Prepare text editor
        unsafe {
            env::set_var("EDITOR", self.config().get_text_editor());
        }
        let placeholder: String = format!("# Type private SSH key for {username}@{host}\n");
        // Put input mode back to normal
        if let Err(err) = self.context_mut().terminal().disable_raw_mode() {
            error!("Could not disable raw mode: {}", err);
        }
        // Leave alternate mode
        if let Err(err) = self.context_mut().terminal().leave_alternate_screen() {
            error!("Could not leave alternate screen: {}", err);
        }
        // Lock ports
        if let Err(err) = self.app.lock_ports() {
            error!("Failed to lock ports: {err}");
        }
        // Write key to file
        let res = match edit::edit(placeholder.as_bytes()) {
            Ok(rsa_key) => {
                // Remove placeholder from `rsa_key`
                let rsa_key: String = rsa_key.as_str().replace(placeholder.as_str(), "");
                if rsa_key.is_empty() {
                    // Report error: empty key
                    Err("SSH key is empty!".to_string())
                } else {
                    self.add_ssh_key(host.as_str(), username.as_str(), rsa_key.as_str())
                        .map_err(|e| format!("Could not create new private key: {e}"))
                }
            }
            Err(err) => {
                // Report error
                Err(format!("Could not write private key to file: {err}"))
            }
        };
        // Restore terminal
        if let Some(ctx) = self.context.as_mut() {
            // Enter alternate mode
            if let Err(err) = ctx.terminal().enter_alternate_screen() {
                error!("Could not enter alternate screen: {}", err);
            }
            // Re-enable raw mode
            if let Err(err) = ctx.terminal().enable_raw_mode() {
                error!("Failed to enter raw mode: {}", err);
            }
            if let Err(err) = ctx.terminal().clear_screen() {
                error!("Could not clear screen screen: {}", err);
            }
            // Unlock ports
            if let Err(err) = self.app.unlock_ports() {
                error!("Failed to unlock ports: {err}");
            }
        }

        res
    }

    /// Given a component and a color, save the color into the theme
    pub(super) fn action_save_color(&mut self, component: IdTheme, color: Color) {
        let theme: &mut Theme = self.theme_mut();
        match component {
            IdTheme::AuthAddress => {
                theme.auth_address = color;
            }
            IdTheme::AuthBookmarks => {
                theme.auth_bookmarks = color;
            }
            IdTheme::AuthPassword => {
                theme.auth_password = color;
            }
            IdTheme::AuthPort => {
                theme.auth_port = color;
            }
            IdTheme::AuthProtocol => {
                theme.auth_protocol = color;
            }
            IdTheme::AuthRecentHosts => {
                theme.auth_recents = color;
            }
            IdTheme::AuthUsername => {
                theme.auth_username = color;
            }
            IdTheme::MiscError => {
                theme.misc_error_dialog = color;
            }
            IdTheme::MiscInfo => {
                theme.misc_info_dialog = color;
            }
            IdTheme::MiscInput => {
                theme.misc_input_dialog = color;
            }
            IdTheme::MiscKeys => {
                theme.misc_keys = color;
            }
            IdTheme::MiscQuit => {
                theme.misc_quit_dialog = color;
            }
            IdTheme::MiscSave => {
                theme.misc_save_dialog = color;
            }
            IdTheme::MiscWarn => {
                theme.misc_warn_dialog = color;
            }
            IdTheme::ExplorerLocalBg => {
                theme.transfer_local_explorer_background = color;
            }
            IdTheme::ExplorerLocalFg => {
                theme.transfer_local_explorer_foreground = color;
            }
            IdTheme::ExplorerLocalHg => {
                theme.transfer_local_explorer_highlighted = color;
            }
            IdTheme::ExplorerRemoteBg => {
                theme.transfer_remote_explorer_background = color;
            }
            IdTheme::ExplorerRemoteFg => {
                theme.transfer_remote_explorer_foreground = color;
            }
            IdTheme::ExplorerRemoteHg => {
                theme.transfer_remote_explorer_highlighted = color;
            }
            IdTheme::LogBg => {
                theme.transfer_log_background = color;
            }
            IdTheme::LogWindow => {
                theme.transfer_log_window = color;
            }
            IdTheme::ProgBarFull => {
                theme.transfer_progress_bar_full = color;
            }
            IdTheme::ProgBarPartial => {
                theme.transfer_progress_bar_partial = color;
            }
            IdTheme::StatusHidden => {
                theme.transfer_status_hidden = color;
            }
            IdTheme::StatusSorting => {
                theme.transfer_status_sorting = color;
            }
            IdTheme::StatusSync => {
                theme.transfer_status_sync_browsing = color;
            }
            _ => {}
        }
    }

    /// Collect values from input and put them into the theme.
    /// If a component has an invalid color, returns Err(component_id)
    fn collect_styles(&mut self) -> Result<(), Id> {
        // auth
        let auth_address = self
            .get_color(&Id::Theme(IdTheme::AuthAddress))
            .map_err(|_| Id::Theme(IdTheme::AuthAddress))?;
        let auth_bookmarks = self
            .get_color(&Id::Theme(IdTheme::AuthBookmarks))
            .map_err(|_| Id::Theme(IdTheme::AuthBookmarks))?;
        let auth_password = self
            .get_color(&Id::Theme(IdTheme::AuthPassword))
            .map_err(|_| Id::Theme(IdTheme::AuthPassword))?;
        let auth_port = self
            .get_color(&Id::Theme(IdTheme::AuthPort))
            .map_err(|_| Id::Theme(IdTheme::AuthPort))?;
        let auth_protocol = self
            .get_color(&Id::Theme(IdTheme::AuthProtocol))
            .map_err(|_| Id::Theme(IdTheme::AuthProtocol))?;
        let auth_recents = self
            .get_color(&Id::Theme(IdTheme::AuthRecentHosts))
            .map_err(|_| Id::Theme(IdTheme::AuthRecentHosts))?;
        let auth_username = self
            .get_color(&Id::Theme(IdTheme::AuthUsername))
            .map_err(|_| Id::Theme(IdTheme::AuthUsername))?;
        // misc
        let misc_error_dialog = self
            .get_color(&Id::Theme(IdTheme::MiscError))
            .map_err(|_| Id::Theme(IdTheme::MiscError))?;
        let misc_info_dialog = self
            .get_color(&Id::Theme(IdTheme::MiscInfo))
            .map_err(|_| Id::Theme(IdTheme::MiscInfo))?;
        let misc_input_dialog = self
            .get_color(&Id::Theme(IdTheme::MiscInput))
            .map_err(|_| Id::Theme(IdTheme::MiscInput))?;
        let misc_keys = self
            .get_color(&Id::Theme(IdTheme::MiscKeys))
            .map_err(|_| Id::Theme(IdTheme::MiscKeys))?;
        let misc_quit_dialog = self
            .get_color(&Id::Theme(IdTheme::MiscQuit))
            .map_err(|_| Id::Theme(IdTheme::MiscQuit))?;
        let misc_save_dialog = self
            .get_color(&Id::Theme(IdTheme::MiscSave))
            .map_err(|_| Id::Theme(IdTheme::MiscSave))?;
        let misc_warn_dialog = self
            .get_color(&Id::Theme(IdTheme::MiscWarn))
            .map_err(|_| Id::Theme(IdTheme::MiscWarn))?;
        // transfer
        let transfer_local_explorer_background = self
            .get_color(&Id::Theme(IdTheme::ExplorerLocalBg))
            .map_err(|_| Id::Theme(IdTheme::ExplorerLocalBg))?;
        let transfer_local_explorer_foreground = self
            .get_color(&Id::Theme(IdTheme::ExplorerLocalFg))
            .map_err(|_| Id::Theme(IdTheme::ExplorerLocalFg))?;
        let transfer_local_explorer_highlighted = self
            .get_color(&Id::Theme(IdTheme::ExplorerLocalHg))
            .map_err(|_| Id::Theme(IdTheme::ExplorerLocalHg))?;
        let transfer_remote_explorer_background = self
            .get_color(&Id::Theme(IdTheme::ExplorerRemoteBg))
            .map_err(|_| Id::Theme(IdTheme::ExplorerRemoteBg))?;
        let transfer_remote_explorer_foreground = self
            .get_color(&Id::Theme(IdTheme::ExplorerRemoteFg))
            .map_err(|_| Id::Theme(IdTheme::ExplorerRemoteFg))?;
        let transfer_remote_explorer_highlighted = self
            .get_color(&Id::Theme(IdTheme::ExplorerRemoteHg))
            .map_err(|_| Id::Theme(IdTheme::ExplorerRemoteHg))?;
        let transfer_log_background = self
            .get_color(&Id::Theme(IdTheme::LogBg))
            .map_err(|_| Id::Theme(IdTheme::LogBg))?;
        let transfer_log_window = self
            .get_color(&Id::Theme(IdTheme::LogWindow))
            .map_err(|_| Id::Theme(IdTheme::LogWindow))?;
        let transfer_progress_bar_full = self
            .get_color(&Id::Theme(IdTheme::ProgBarFull))
            .map_err(|_| Id::Theme(IdTheme::ProgBarFull))?;
        let transfer_progress_bar_partial = self
            .get_color(&Id::Theme(IdTheme::ProgBarPartial))
            .map_err(|_| Id::Theme(IdTheme::ProgBarPartial))?;
        let transfer_status_hidden = self
            .get_color(&Id::Theme(IdTheme::StatusHidden))
            .map_err(|_| Id::Theme(IdTheme::StatusHidden))?;
        let transfer_status_sorting = self
            .get_color(&Id::Theme(IdTheme::StatusSorting))
            .map_err(|_| Id::Theme(IdTheme::StatusSorting))?;
        let transfer_status_sync_browsing = self
            .get_color(&Id::Theme(IdTheme::StatusSync))
            .map_err(|_| Id::Theme(IdTheme::StatusSync))?;
        // Update theme
        let theme = self.theme_mut();
        theme.auth_address = auth_address;
        theme.auth_bookmarks = auth_bookmarks;
        theme.auth_password = auth_password;
        theme.auth_port = auth_port;
        theme.auth_protocol = auth_protocol;
        theme.auth_recents = auth_recents;
        theme.auth_username = auth_username;
        theme.misc_error_dialog = misc_error_dialog;
        theme.misc_info_dialog = misc_info_dialog;
        theme.misc_input_dialog = misc_input_dialog;
        theme.misc_keys = misc_keys;
        theme.misc_quit_dialog = misc_quit_dialog;
        theme.misc_save_dialog = misc_save_dialog;
        theme.misc_warn_dialog = misc_warn_dialog;
        theme.transfer_local_explorer_background = transfer_local_explorer_background;
        theme.transfer_local_explorer_foreground = transfer_local_explorer_foreground;
        theme.transfer_local_explorer_highlighted = transfer_local_explorer_highlighted;
        theme.transfer_remote_explorer_background = transfer_remote_explorer_background;
        theme.transfer_remote_explorer_foreground = transfer_remote_explorer_foreground;
        theme.transfer_remote_explorer_highlighted = transfer_remote_explorer_highlighted;
        theme.transfer_log_background = transfer_log_background;
        theme.transfer_log_window = transfer_log_window;
        theme.transfer_progress_bar_full = transfer_progress_bar_full;
        theme.transfer_progress_bar_partial = transfer_progress_bar_partial;
        theme.transfer_status_hidden = transfer_status_hidden;
        theme.transfer_status_sorting = transfer_status_sorting;
        theme.transfer_status_sync_browsing = transfer_status_sync_browsing;
        Ok(())
    }

    /// Get color from component
    fn get_color(&self, component: &Id) -> Result<Color, ()> {
        match self.app.state(component) {
            Ok(State::Single(StateValue::String(color))) => {
                match crate::utils::parser::parse_color(color.as_str()) {
                    Some(c) => Ok(c),
                    None => Err(()),
                }
            }
            _ => Err(()),
        }
    }
}
