#![allow(dead_code)]

use std::{
    path::Path,
    sync::{Arc, RwLock},
};

use crossbeam_channel::Sender;
use cuba_lib::{
    core::cuba::Cuba,
    send_error,
    shared::{
        config::{
            AccessMode, CONFIG_FILE, Config, ConfigEntryKey, ConfigEntryMut, ConfigEntryType,
            config_path,
        },
        config_writer::ConfigWriter,
        message::{Message, StringError},
        npath::{Abs, Dir, Rel},
    },
};

use crate::{
    AppView, ViewId,
    egui_widgets::{GlobListWidget, NPathEditor, NPathEditorBuffer, build_row, label_value_table},
    password_ids::PasswordIDs,
};

/// Defines a `ConfigView`.
pub struct ConfigView {
    cuba: Arc<RwLock<Cuba>>,
    sender: Sender<Arc<dyn Message>>,
    password_ids: Arc<PasswordIDs>,
    selected_config_entry_key: Option<ConfigEntryKey>,
    npath_editor_buffer: NPathEditorBuffer,
    add_entry_type: ConfigEntryType,
    entry_name: String,
    config: Option<Config>,
}

/// Methods of `ConfigView`.
impl ConfigView {
    /// Creates a new `ConfigView`.
    pub fn new(
        cuba: Arc<RwLock<Cuba>>,
        sender: Sender<Arc<dyn Message>>,
        password_ids: Arc<PasswordIDs>,
    ) -> Self {
        Self {
            cuba,
            sender,
            password_ids,
            selected_config_entry_key: None,
            npath_editor_buffer: NPathEditorBuffer::new(),
            add_entry_type: ConfigEntryType::LocalFS,
            entry_name: String::new(),
            config: None,
        }
    }
}

/// Methods of `ConfigView`.
impl ConfigView {
    /// Reload the config.
    pub fn reload_config(&mut self) {
        if let Ok(guard) = self.cuba.try_read() {
            if let Some(config) = guard.config() {
                self.config = Some(config.clone());
            } else {
                send_error!(
                    self.sender,
                    StringError::new("No config loaded".to_string())
                );
            }
        } else {
            send_error!(
                self.sender,
                StringError::new("Config locked by task".to_string())
            );
        }
    }

    /// Renders the config entry_editor.
    fn render_entry_editor(&mut self, ui: &mut egui::Ui) {
        // Set row height.
        let row_height: f32 = 25.0;

        // Horizontal layout (buttons).
        ui.horizontal(|ui| {
            if let Some(entry_key) = &self.selected_config_entry_key {
                // The heading.
                ui.heading(entry_key.to_string());

                // Add stretch.
                ui.add_space(ui.available_width() - 100.0);

                // The delete entry button.
                if ui.button("Delete Entry").clicked()
                    && let Some(config) = &mut self.config
                {
                    config.delete_entry(entry_key);
                    self.selected_config_entry_key = None;
                    self.entry_name.clear();
                }
            } else {
                ui.heading("".to_string());
            }
        });

        // Separator.
        ui.separator();

        // Vertical layout (config entry content).
        egui::ScrollArea::vertical()
            .id_salt("Config content")
            .show(ui, |ui| {
                if let Some(config) = &mut self.config {
                    let fs_entries = config.list_fs_keys();

                    if let Some(entry_key) = &self.selected_config_entry_key
                        && let Some(entry) = config.get_entry_mut(entry_key)
                    {
                        match entry {
                            ConfigEntryMut::LocalFS(local_fs) => {
                                // Set label width.
                                let label_width = egui_extras::Size::exact(60.0);

                                // The local fs table.
                                label_value_table(ui, 2, row_height, |rows| {
                                    // The name row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Name:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(
                                                egui::TextEdit::singleline(&mut self.entry_name)
                                                    .desired_width(f32::INFINITY),
                                            );
                                        },
                                    );

                                    // The dir row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Dir:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(NPathEditor::<Abs, Dir>::new(
                                                &entry_key.to_string(),
                                                &mut local_fs.dir,
                                                &mut self.npath_editor_buffer,
                                            ));
                                        },
                                    );
                                });
                            }
                            ConfigEntryMut::WebDAVFS(webdav_fs) => {
                                // The label width.
                                let label_width = egui_extras::Size::exact(120.0);

                                // The WebDAV fs table.
                                label_value_table(ui, 5, row_height, |rows| {
                                    // The name row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Name:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(
                                                egui::TextEdit::singleline(&mut self.entry_name)
                                                    .desired_width(f32::INFINITY),
                                            );
                                        },
                                    );

                                    // The url row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Url:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(NPathEditor::<Abs, Dir>::new(
                                                &entry_key.to_string(),
                                                &mut webdav_fs.url,
                                                &mut self.npath_editor_buffer,
                                            ));
                                        },
                                    );

                                    // The user row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "User:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(
                                                egui::TextEdit::singleline(&mut webdav_fs.user)
                                                    .desired_width(f32::INFINITY),
                                            );
                                        },
                                    );

                                    // The password id row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Password ID:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            egui::ComboBox::from_id_salt("PasswordID")
                                                .selected_text(webdav_fs.password_id.to_string())
                                                .show_ui(ui, |ui| {
                                                    for password_id in &self.password_ids.get() {
                                                        ui.selectable_value(
                                                            &mut webdav_fs.password_id,
                                                            password_id.to_string(),
                                                            password_id,
                                                        );
                                                    }
                                                });
                                        },
                                    );

                                    // The timeout row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Timeout (secs):",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(egui::DragValue::new(
                                                &mut webdav_fs.timeout_secs,
                                            ));
                                        },
                                    );
                                });
                            }
                            ConfigEntryMut::Backup(backup) => {
                                // The label width.
                                let label_width = egui_extras::Size::exact(120.0);

                                // The backup table.
                                label_value_table(ui, 10, row_height, |rows| {
                                    // The name row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Name:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(
                                                egui::TextEdit::singleline(&mut self.entry_name)
                                                    .desired_width(f32::INFINITY),
                                            );
                                        },
                                    );

                                    // The source fs row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Source:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            egui::ComboBox::from_id_salt("SourceFS")
                                                .selected_text(backup.src_fs.to_string())
                                                .show_ui(ui, |ui| {
                                                    for fs_entry in &fs_entries {
                                                        ui.selectable_value(
                                                            &mut backup.src_fs,
                                                            fs_entry.name.to_string(),
                                                            fs_entry.name.to_string(),
                                                        );
                                                    }
                                                });
                                        },
                                    );

                                    // The destination fs row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Destination:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            egui::ComboBox::from_id_salt("DestFS")
                                                .selected_text(backup.dest_fs.to_string())
                                                .show_ui(ui, |ui| {
                                                    for fs_entry in &fs_entries {
                                                        ui.selectable_value(
                                                            &mut backup.dest_fs,
                                                            fs_entry.name.to_string(),
                                                            fs_entry.name.to_string(),
                                                        );
                                                    }
                                                });
                                        },
                                    );

                                    // The source dir row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Source dir:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(NPathEditor::<Rel, Dir>::new(
                                                &(entry_key.to_string() + ".src"),
                                                &mut backup.src_dir,
                                                &mut self.npath_editor_buffer,
                                            ));
                                        },
                                    );

                                    // The destination dir row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Destination dir:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(NPathEditor::<Rel, Dir>::new(
                                                &(entry_key.to_string() + ".dest"),
                                                &mut backup.dest_dir,
                                                &mut self.npath_editor_buffer,
                                            ));
                                        },
                                    );

                                    // The compression row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Compression",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.checkbox(&mut backup.compression, "");
                                        },
                                    );

                                    // The encryption row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Encryption",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.checkbox(&mut backup.encrypt, "");
                                        },
                                    );

                                    // The password id row.
                                    if backup.encrypt {
                                        let password_id =
                                            backup.password_id.get_or_insert_with(String::new);

                                        build_row(
                                            rows,
                                            label_width,
                                            "Password ID:",
                                            egui_extras::Size::remainder(),
                                            |ui| {
                                                egui::ComboBox::from_id_salt("PasswordID")
                                                    .selected_text(password_id.as_str())
                                                    .show_ui(ui, |ui| {
                                                        for id in &self.password_ids.get() {
                                                            ui.selectable_value(
                                                                password_id,
                                                                id.clone(),
                                                                id,
                                                            );
                                                        }
                                                    });
                                            },
                                        );
                                    } else {
                                        backup.password_id = None;
                                    }

                                    // The include row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Include:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(GlobListWidget::new(&mut backup.include));
                                        },
                                    );

                                    // The exclude row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Exclude:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(GlobListWidget::new(&mut backup.exclude));
                                        },
                                    );
                                });
                            }
                            ConfigEntryMut::Restore(restore) => {
                                // The label width.
                                let label_width = egui_extras::Size::exact(160.0);

                                // The restore table.
                                label_value_table(ui, 11, row_height, |rows| {
                                    // The name row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Name:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(
                                                egui::TextEdit::singleline(&mut self.entry_name)
                                                    .desired_width(f32::INFINITY),
                                            );
                                        },
                                    );

                                    // The source fs row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Source:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            egui::ComboBox::from_id_salt("SourceFS")
                                                .selected_text(restore.src_fs.to_string())
                                                .show_ui(ui, |ui| {
                                                    for fs_entry in &fs_entries {
                                                        ui.selectable_value(
                                                            &mut restore.src_fs,
                                                            fs_entry.name.to_string(),
                                                            fs_entry.name.to_string(),
                                                        );
                                                    }
                                                });
                                        },
                                    );

                                    // The destination fs row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Destination:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            egui::ComboBox::from_id_salt("DestFS")
                                                .selected_text(restore.dest_fs.to_string())
                                                .show_ui(ui, |ui| {
                                                    for fs_entry in &fs_entries {
                                                        ui.selectable_value(
                                                            &mut restore.dest_fs,
                                                            fs_entry.name.to_string(),
                                                            fs_entry.name.to_string(),
                                                        );
                                                    }
                                                });
                                        },
                                    );

                                    // The source dir row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Source dir:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(NPathEditor::<Rel, Dir>::new(
                                                &(entry_key.to_string() + ".src"),
                                                &mut restore.src_dir,
                                                &mut self.npath_editor_buffer,
                                            ));
                                        },
                                    );

                                    // The destination dir row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Destination dir:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(NPathEditor::<Rel, Dir>::new(
                                                &(entry_key.to_string() + ".dest"),
                                                &mut restore.dest_dir,
                                                &mut self.npath_editor_buffer,
                                            ));
                                        },
                                    );

                                    // The include row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Include:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(GlobListWidget::new(&mut restore.include));
                                        },
                                    );

                                    // The exclude row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Exclude:",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.add(GlobListWidget::new(&mut restore.exclude));
                                        },
                                    );

                                    // The restore timestamps row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Restore Timestamps",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.checkbox(&mut restore.restore_timestamps, "");
                                        },
                                    );

                                    // The force override row.
                                    build_row(
                                        rows,
                                        label_width,
                                        "Force Override",
                                        egui_extras::Size::remainder(),
                                        |ui| {
                                            ui.checkbox(&mut restore.force_override, "");
                                        },
                                    );
                                });
                            }
                        }
                    }
                }
            });
    }
}

/// Impl of `AppView` for `ConfigView`.
impl AppView for ConfigView {
    /// Returns the name of the view.
    fn name(&self) -> &str {
        "Config"
    }

    /// Returns the view id.
    fn view_id(&self) -> ViewId {
        ViewId::Config
    }

    /// Post init.
    fn post_init(&mut self) {
        self.reload_config();
    }

    /// Renders the view for egui.
    fn ui(&mut self, ui: &mut egui::Ui) {
        let height = ui.available_height();

        // Horizontal layout (config entry list, entry content).
        ui.horizontal(|ui| {
            // Vertical layout (heading, list, buttons).
            ui.vertical(|ui| {
                // Set width/height.
                ui.set_width(410.0);
                ui.set_height(height);

                // Entry list heading.
                ui.heading("Entries");

                // Separator.
                ui.separator();

                // Rename entry.
                if let Some(entry_key) = &self.selected_config_entry_key
                    && entry_key.name != self.entry_name
                    && let Some(config) = &mut self.config
                {
                    config.rename_entry(entry_key, self.entry_name.as_str());
                    self.selected_config_entry_key = Some(ConfigEntryKey::new(
                        entry_key.entry_type.clone(),
                        self.entry_name.clone(),
                    ));
                }

                // The footer height.
                let footer_height = 35.0;

                // The max scroll area height.
                let max_scroll_area_height = ui.available_height() - footer_height;

                // Entry list.
                egui::ScrollArea::vertical()
                    .auto_shrink([false; 2])
                    .max_height(max_scroll_area_height)
                    .id_salt("Entries")
                    .show(ui, |ui| {
                        if let Some(config) = &self.config {
                            for entry_key in config.list_entry_keys() {
                                let selected =
                                    self.selected_config_entry_key == Some(entry_key.clone());

                                if ui
                                    .selectable_label(selected, format!("{}", entry_key))
                                    .clicked()
                                {
                                    self.selected_config_entry_key = Some(entry_key.clone());
                                    self.entry_name = entry_key.name;
                                }
                            }
                        }
                    });

                // Stretches to fill space.
                let remaining = ui.available_height() - footer_height;
                ui.add_space(remaining);

                // Separator.
                ui.separator();

                // Horizontal layout (footer).
                ui.horizontal(|ui| {
                    if ui.button("+ Add").clicked()
                        && let Some(config) = &mut self.config
                    {
                        config.add_new_entry(&self.add_entry_type, "New Entry");
                    }

                    // The entries which can be added
                    egui::ComboBox::from_id_salt("add_entry_type")
                        .selected_text(self.add_entry_type.to_string())
                        .show_ui(ui, |ui| {
                            for entry_type in ConfigEntryType::ALL {
                                ui.selectable_value(
                                    &mut self.add_entry_type,
                                    entry_type.clone(),
                                    entry_type.to_string(),
                                );
                            }
                        });

                    // Add stretch.
                    //ui.add_space(ui.available_width() - 65.0);

                    // The refresh config button.
                    if ui.button("Reload Config").clicked() {
                        self.reload_config();
                    }

                    // The save config button.
                    if ui.button("Save Config").clicked()
                        && let Some(config) = &self.config
                    {
                        if let Ok(mut guard) = self.cuba.try_write() {
                            guard.set_config(config.clone());

                            match config_path(AccessMode::Write) {
                                Ok(config_path) => {
                                    let config_file = config_path.join(Path::new(CONFIG_FILE));

                                    ConfigWriter::update(self.sender.clone(), &config_file, config);
                                }
                                Err(error) => {
                                    send_error!(self.sender, error);
                                }
                            }
                        } else {
                            send_error!(
                                self.sender,
                                StringError::new(
                                    "Could not save. Config locked by task".to_string()
                                )
                            );
                        }
                    }
                });
            });

            // Separator.
            ui.separator();

            // Vertical layout (config entry content).
            ui.vertical(|ui| {
                ui.set_height(height);

                self.render_entry_editor(ui);
            });
        });
    }
}
