// File: ./src/gui/update/settings.rs
// SPDX-License-Identifier: GPL-3.0-or-later
// Renders the settings and onboarding screens.

rust_i18n::i18n!("../locales", fallback = "en");

use crate::cache::Cache;

use crate::gui::async_ops::*;
use crate::gui::message::Message;
use crate::gui::state::{AppState, GuiApp};
use crate::gui::update::common::{apply_alias_retroactively, refresh_filtered_tasks, save_config};
use crate::model::parser::{format_duration_compact, parse_duration, validate_alias_integrity};
use crate::storage::{LOCAL_CALENDAR_HREF, LocalCalendarRegistry, LocalStorage};
use iced::Task;

pub fn handle(app: &mut GuiApp, message: Message) -> Task<Message> {
    match message {
        Message::ConfigLoaded(Ok(config_box)) => {
            let config = *config_box;
            app.core_config = config.clone();
            app.ob_password_visible = false;
            let locals = LocalCalendarRegistry::load(app.ctx.as_ref()).unwrap_or_default();
            app.local_cals_editing = locals.clone();

            app.remote_cals_editing = app
                .calendars
                .iter()
                .filter(|c| !c.href.starts_with("local://"))
                .cloned()
                .collect();

            app.hidden_calendars = config.hidden_calendars.clone().into_iter().collect();
            app.disabled_calendars = config.disabled_calendars.clone().into_iter().collect();
            app.sort_cutoff_months = config.sort_cutoff_months;
            app.ob_sort_months_input = match config.sort_cutoff_months {
                Some(m) => m.to_string(),
                None => "".to_string(),
            };
            app.ob_insecure = config.allow_insecure_certs;
            app.tag_aliases = config.tag_aliases.clone();
            app.hide_completed = config.hide_completed;
            app.hide_fully_completed_tags = config.hide_fully_completed_tags;
            app.hide_aliases_in_sidebar = config.hide_aliases_in_sidebar;
            app.sort_standard_by_priority = config.sort_standard_by_priority;
            app.sort_preset = config.sort_preset;
            app.ui_scale = config.ui_scale;
            app.show_priority_numbers = config.show_priority_numbers;
            app.sync_settings = config.sync_settings;
            app.current_theme = config.theme;

            app.quick_filter_term = config.quick_filter_term.clone();
            app.quick_filter_icon = config.quick_filter_icon.clone();
            app.show_quick_filter = config.show_quick_filter;
            app.sidebar_is_hidden = config.sidebar_is_hidden;
            app.ob_quick_filter_term_input = config.quick_filter_term.clone();
            app.ob_quick_filter_icon_input = config.quick_filter_icon.clone();

            app.ob_url = config.url.clone();
            app.ob_user = config.username.clone();
            app.ob_pass = config.password.clone();
            app.ob_default_cal = config.default_calendar.clone();
            app.urgent_days = config.urgent_days_horizon;
            app.urgent_prio = config.urgent_priority_threshold;
            app.default_priority = config.default_priority;
            app.start_grace_period_days = config.start_grace_period_days;
            app.ob_urgent_days_input = app.urgent_days.to_string();
            app.ob_urgent_prio_input = app.urgent_prio.to_string();
            app.ob_default_priority_input = app.default_priority.to_string();
            app.ob_start_grace_input = app.start_grace_period_days.to_string();

            app.auto_reminders = config.auto_reminders;
            app.default_reminder_time = config.default_reminder_time.clone();
            app.snooze_short_mins = config.snooze_short_mins;
            app.create_events_for_tasks = config.create_events_for_tasks;
            app.delete_events_on_completion = config.delete_events_on_completion;
            app.snooze_long_mins = config.snooze_long_mins;
            app.auto_refresh_interval_mins = config.auto_refresh_interval_mins;
            app.strikethrough_completed = config.strikethrough_completed;
            app.trash_retention_days = config.trash_retention_days;

            app.ob_snooze_short_input = format_duration_compact(config.snooze_short_mins);
            app.ob_snooze_long_input = format_duration_compact(config.snooze_long_mins);
            app.ob_auto_refresh_input = format_duration_compact(config.auto_refresh_interval_mins);
            app.ob_trash_retention_input = config.trash_retention_days.to_string();

            app.ob_max_done_roots_input = config.max_done_roots.to_string();
            app.ob_max_done_subtasks_input = config.max_done_subtasks.to_string();

            app.session.expanded_tags = config.expanded_tags.clone();
            app.session.expanded_locations = config.expanded_locations.clone();
            app.session.expanded_done_groups = config.expanded_done_groups.clone();

            app.pinned_actions = config.pinned_actions.clone();
            app.log_level = config.log_level;

            // Apply the configured log level
            crate::system::set_log_level(config.log_level.to_level_filter());

            let mut cached_cals = Cache::load_calendars(app.ctx.as_ref()).unwrap_or_default();

            for loc in locals {
                if !cached_cals.iter().any(|c| c.href == loc.href) {
                    cached_cals.push(loc);
                }
            }
            app.calendars = cached_cals;
            app.sort_calendars();

            app.store.clear();

            for cal in &app.calendars {
                if cal.href.starts_with("local://") {
                    if let Ok(mut tasks) = LocalStorage::load_for_href(app.ctx.as_ref(), &cal.href)
                    {
                        crate::journal::Journal::apply_to_tasks(
                            app.ctx.as_ref(),
                            &mut tasks,
                            &cal.href,
                        );
                        app.store.insert(cal.href.clone(), tasks);
                    }
                } else if let Ok((mut tasks, _)) = Cache::load(app.ctx.as_ref(), &cal.href) {
                    crate::journal::Journal::apply_to_tasks(
                        app.ctx.as_ref(),
                        &mut tasks,
                        &cal.href,
                    );
                    app.store.insert(cal.href.clone(), tasks);
                }
            }

            let mut target_href = None;
            if let Some(def) = &app.ob_default_cal
                && let Some(cal) = app
                    .calendars
                    .iter()
                    .find(|c| c.name == *def || c.href == *def)
            {
                if app.hidden_calendars.contains(&cal.href) {
                    app.hidden_calendars.remove(&cal.href);
                }
                target_href = Some(cal.href.clone());
            }

            if target_href.is_none() {
                target_href = Some(LOCAL_CALENDAR_HREF.to_string());
            }
            app.active_cal_href = target_href.clone();
            app.ob_default_cal = target_href;

            refresh_filtered_tasks(app);
            app.state = AppState::Active;
            app.loading = true;
            Task::perform(connect_and_fetch_wrapper(app.ctx.clone(), config), |res| {
                Message::Loaded(res.map_err(|e| e.to_string()))
            })
        }
        Message::ConfigLoaded(Err(e)) => {
            app.state = AppState::Onboarding;
            if !e.contains("Config file not found") {
                app.error_msg = Some(
                    rust_i18n::t!("config_error_prefix", error = e)
                        .to_string()
                        .trim()
                        .to_owned(),
                );
                app.config_was_corrupted = true;
            }
            Task::none()
        }
        Message::ObUrlChanged(v) => {
            app.ob_url = v;
            Task::none()
        }
        Message::ObUserChanged(v) => {
            app.ob_user = v;
            Task::none()
        }
        Message::ObPassChanged(v) => {
            app.ob_pass = v;
            Task::none()
        }
        Message::ToggleObPasswordVisibility => {
            app.ob_password_visible = !app.ob_password_visible;
            Task::none()
        }
        Message::ObDefaultCalChanged(v) => {
            app.ob_default_cal = Some(v);
            save_config(app);
            Task::none()
        }
        Message::ObInsecureToggled(val) => {
            app.ob_insecure = val;
            Task::none()
        }
        Message::ThemeChanged(theme) => {
            app.current_theme = theme;
            save_config(app);
            Task::none()
        }
        Message::ObSubmit => {
            app.ob_password_visible = false;
            app.calendars.retain(|c| !c.href.starts_with("local://"));
            app.calendars.extend(app.local_cals_editing.clone());
            app.sort_calendars();

            if app.ob_sort_months_input.trim().is_empty() {
                app.sort_cutoff_months = None;
            } else if let Ok(n) = app.ob_sort_months_input.trim().parse::<u32>() {
                app.sort_cutoff_months = Some(n);
            }

            let config_to_save = save_config(app);

            app.state = AppState::Loading;
            app.error_msg = Some(rust_i18n::t!("connecting").to_string());

            Task::perform(
                connect_and_fetch_wrapper(app.ctx.clone(), config_to_save),
                |res| Message::Loaded(res.map_err(|e| e.to_string())),
            )
        }
        Message::OpenSettings => {
            let cfg = &app.core_config;
            app.ob_password_visible = false;
            app.ob_url = cfg.url.clone();
            app.ob_user = cfg.username.clone();
            // app.ob_pass is already securely held in memory from startup

            app.remote_cals_editing = app
                .calendars
                .iter()
                .filter(|c| !c.href.starts_with("local://"))
                .cloned()
                .collect();

            let mut target_href = cfg.default_calendar.clone();
            if let Some(ref def) = target_href
                && let Some(found) = app
                    .calendars
                    .iter()
                    .find(|c| c.name == *def || c.href == *def)
            {
                target_href = Some(found.href.clone());
            }
            app.ob_default_cal = target_href;
            app.hide_completed = cfg.hide_completed;
            app.hide_fully_completed_tags = cfg.hide_fully_completed_tags;
            app.hide_aliases_in_sidebar = cfg.hide_aliases_in_sidebar;
            app.ob_insecure = cfg.allow_insecure_certs;
            app.hidden_calendars = cfg.hidden_calendars.iter().cloned().collect();
            app.tag_aliases = cfg.tag_aliases.clone();
            app.sort_cutoff_months = cfg.sort_cutoff_months;
            app.current_theme = cfg.theme;
            app.ob_sort_months_input = match cfg.sort_cutoff_months {
                Some(m) => m.to_string(),
                None => "".to_string(),
            };
            app.trash_retention_days = cfg.trash_retention_days;
            app.ob_trash_retention_input = cfg.trash_retention_days.to_string();

            app.ob_quick_filter_term_input = cfg.quick_filter_term.clone();
            app.ob_quick_filter_icon_input = cfg.quick_filter_icon.clone();
            app.show_quick_filter = cfg.show_quick_filter;
            app.sidebar_is_hidden = cfg.sidebar_is_hidden;
            app.log_level = cfg.log_level;
            app.state = AppState::Settings;

            app.editing_alias_key = None;
            app.alias_input_key.clear();
            app.alias_input_values.clear();

            Task::none()
        }
        Message::CancelSettings => {
            app.ob_password_visible = false;
            app.calendars.retain(|c| !c.href.starts_with("local://"));
            app.calendars.extend(app.local_cals_editing.clone());
            app.sort_calendars();

            app.editing_alias_key = None;
            app.alias_input_key.clear();
            app.alias_input_values.clear();

            save_config(app);
            refresh_filtered_tasks(app);
            app.state = AppState::Active;
            Task::none()
        }
        Message::ObSubmitOffline => {
            app.ob_password_visible = false;
            app.ob_url.clear();
            app.ob_user.clear();
            app.ob_pass.clear();

            let config_to_save = save_config(app);

            app.state = AppState::Loading;
            Task::perform(
                connect_and_fetch_wrapper(app.ctx.clone(), config_to_save),
                |res| Message::Loaded(res.map_err(|e| e.to_string())),
            )
        }
        Message::AliasKeyInput(v) => {
            app.alias_input_key = v;
            Task::none()
        }
        Message::AliasValueInput(v) => {
            app.alias_input_values = v;
            Task::none()
        }
        Message::EditAlias(key, vals) => {
            app.editing_alias_key = Some(key.clone());
            app.alias_input_key = if key.starts_with("@@") {
                key
            } else {
                format!("#{}", key)
            };
            app.alias_input_values = vals;
            Task::none()
        }
        Message::CancelEditAlias => {
            app.editing_alias_key = None;
            app.alias_input_key.clear();
            app.alias_input_values.clear();
            Task::none()
        }
        Message::AddAlias => {
            if !app.alias_input_key.is_empty() && !app.alias_input_values.is_empty() {
                let tags = crate::model::parser::parse_alias_values(&app.alias_input_values);

                if !tags.is_empty() {
                    let raw_key = app.alias_input_key.trim();
                    let key = if raw_key.starts_with("@@") {
                        raw_key.to_string()
                    } else if raw_key.to_lowercase().starts_with("loc:") {
                        format!("@@{}", raw_key[4..].trim())
                    } else {
                        raw_key.trim_start_matches('#').to_string()
                    };

                    match validate_alias_integrity(&key, &tags, &app.tag_aliases) {
                        Ok(_) => {
                            // If we were editing and changed the key, remove the old one
                            if let Some(old_key) = &app.editing_alias_key
                                && old_key != &key
                            {
                                app.tag_aliases.remove(old_key);
                            }
                            app.editing_alias_key = None;

                            app.tag_aliases.insert(key.clone(), tags.clone());
                            app.alias_input_key.clear();
                            app.alias_input_values.clear();
                            app.error_msg = None;
                            save_config(app);

                            let modified_tasks = apply_alias_retroactively(app, &key, &tags);
                            if !modified_tasks.is_empty() {
                                let actions: Vec<_> = modified_tasks
                                    .into_iter()
                                    .map(crate::journal::Action::Update)
                                    .collect();
                                if let Some(tx) = &app.bg_tx {
                                    let _ = tx.try_send(
                                        crate::gui::async_ops::WorkerCommand::Batch(actions),
                                    );
                                }
                            }
                        }
                        Err(e) => {
                            app.error_msg = Some(format!(
                                "{}",
                                rust_i18n::t!("error_adding_alias", error = e)
                            ));
                        }
                    }
                }
            }
            Task::none()
        }
        Message::RemoveAlias(key) => {
            if app.editing_alias_key.as_ref() == Some(&key) {
                app.editing_alias_key = None;
                app.alias_input_key.clear();
                app.alias_input_values.clear();
            }
            app.tag_aliases.remove(&key);
            save_config(app);
            Task::none()
        }
        Message::ObSortMonthsChanged(val) => {
            if val.is_empty() || val.chars().all(|c| c.is_numeric()) {
                app.ob_sort_months_input = val.clone();

                if val.trim().is_empty() {
                    app.sort_cutoff_months = None;
                } else if let Ok(n) = val.trim().parse::<u32>() {
                    app.sort_cutoff_months = Some(n);
                }
                save_config(app);
                refresh_filtered_tasks(app);
            }
            Task::none()
        }
        Message::ObUrgentDaysChanged(val) => {
            if val.is_empty() || val.chars().all(|c| c.is_numeric()) {
                app.ob_urgent_days_input = val.clone();
                if let Ok(n) = val.trim().parse::<u32>() {
                    app.urgent_days = n;
                    save_config(app);
                    refresh_filtered_tasks(app);
                }
            }
            Task::none()
        }
        Message::ObUrgentPrioChanged(val) => {
            if val.is_empty() || val.chars().all(|c| c.is_numeric()) {
                app.ob_urgent_prio_input = val.clone();
                if let Ok(n) = val.trim().parse::<u8>() {
                    app.urgent_prio = n;
                    save_config(app);
                    refresh_filtered_tasks(app);
                }
            }
            Task::none()
        }
        Message::ObDefaultPriorityChanged(val) => {
            if val.is_empty() || val.chars().all(|c| c.is_numeric()) {
                app.ob_default_priority_input = val.clone();
                if let Ok(n) = val.trim().parse::<u8>()
                    && n > 0
                {
                    app.default_priority = n;
                    save_config(app);
                    refresh_filtered_tasks(app);
                }
            }
            Task::none()
        }
        Message::ObStartGraceChanged(val) => {
            if val.is_empty() || val.chars().all(|c| c.is_numeric()) {
                app.ob_start_grace_input = val.clone();
                if let Ok(n) = val.trim().parse::<u32>() {
                    app.start_grace_period_days = n;
                    save_config(app);
                    refresh_filtered_tasks(app);
                }
            }
            Task::none()
        }
        Message::SetAutoReminders(val) => {
            app.auto_reminders = val;
            save_config(app);
            Task::none()
        }
        Message::SetDefaultReminderTime(val) => {
            app.default_reminder_time = val;
            save_config(app);
            Task::none()
        }
        Message::SetSnoozeShort(val) => {
            app.ob_snooze_short_input = val.clone();
            if let Some(n) = parse_duration(&val) {
                app.snooze_short_mins = n;
                save_config(app);
            }
            Task::none()
        }
        Message::SetSnoozeLong(val) => {
            app.ob_snooze_long_input = val.clone();
            if let Some(n) = parse_duration(&val) {
                app.snooze_long_mins = n;
                save_config(app);
            }
            Task::none()
        }
        Message::SetTrashRetention(val) => {
            if val.is_empty() || val.chars().all(|c| c.is_numeric()) {
                app.ob_trash_retention_input = val.clone();
                if let Ok(n) = val.trim().parse::<u32>() {
                    app.trash_retention_days = n;
                    save_config(app);
                }
            }
            Task::none()
        }
        Message::SetAutoRefreshInterval(val) => {
            app.ob_auto_refresh_input = val.clone();
            if let Some(n) = parse_duration(&val) {
                app.auto_refresh_interval_mins = n;
                save_config(app);
            }
            Task::none()
        }

        Message::SetLanguage(lang) => {
            // Persist the user's language selection in the GUI app state.
            // `lang == "auto"` means follow system/default.
            let lang_opt = if lang == "auto" {
                None
            } else {
                Some(lang.clone())
            };
            app.language = lang_opt.clone();

            // Apply immediately to rust_i18n
            if let Some(ref l) = lang_opt {
                rust_i18n::set_locale(l);
            } else if let Some(sys_lang) = sys_locale::get_locale() {
                rust_i18n::set_locale(sys_lang.split('-').next().unwrap_or("en"));
            }

            // Also save the selection into the persistent Config so it's cross-platform.
            // Load existing config (or default), set language, then save.
            let mut cfg = app.core_config.clone();
            cfg.language = app.language.clone();
            app.core_config = cfg.clone();
            let _ = cfg.save(app.ctx.as_ref());

            Task::none()
        }
        Message::ToggleAdvancedSettings(val) => {
            app.show_advanced_settings = val;
            Task::none()
        }
        Message::SetMaxDoneRoots(val) => {
            if val.is_empty() || val.chars().all(|c| c.is_numeric()) {
                app.ob_max_done_roots_input = val;
                save_config(app);
                refresh_filtered_tasks(app);
            }
            Task::none()
        }
        Message::SetMaxDoneSubtasks(val) => {
            if val.is_empty() || val.chars().all(|c| c.is_numeric()) {
                app.ob_max_done_subtasks_input = val;
                save_config(app);
                refresh_filtered_tasks(app);
            }
            Task::none()
        }
        Message::SetShowPriorityNumbers(val) => {
            app.show_priority_numbers = val;
            save_config(app);
            refresh_filtered_tasks(app);
            Task::none()
        }
        Message::SetSyncSettings(val) => {
            app.sync_settings = val;
            save_config(app);
            Task::none()
        }
        Message::SetLogLevel(level) => {
            app.log_level = level;
            app.core_config.log_level = level;
            save_config(app);
            // Apply the new log level immediately
            crate::system::set_log_level(level.to_level_filter());
            Task::none()
        }

        Message::SetCreateEventsForTasks(val) => {
            let was_disabled = !app.create_events_for_tasks;
            app.create_events_for_tasks = val;
            save_config(app);

            if val
                && was_disabled
                && let Some(client) = &app.client
            {
                let all_tasks: Vec<_> = app
                    .store
                    .calendars
                    .values()
                    .flat_map(|m| m.values())
                    .cloned()
                    .collect();

                let delete_on_completion = app.delete_events_on_completion;

                return Task::perform(
                    async_backfill_events_wrapper(
                        client.clone(),
                        all_tasks,
                        val,
                        delete_on_completion,
                    ),
                    Message::BackfillEventsComplete,
                );
            }
            Task::none()
        }
        Message::SetDeleteEventsOnCompletion(val) => {
            app.delete_events_on_completion = val;
            save_config(app);
            Task::none()
        }

        Message::SetStrikethroughCompleted(val) => {
            app.strikethrough_completed = val;
            save_config(app);
            Task::none()
        }
        Message::TogglePinnedAction(action, enabled) => {
            if enabled {
                if !app.pinned_actions.contains(&action) {
                    app.pinned_actions.push(action);
                }
            } else {
                app.pinned_actions.retain(|a| *a != action);
            }
            crate::gui::update::common::save_config(app);
            Task::none()
        }
        Message::SetShowQuickFilter(val) => {
            app.show_quick_filter = val;
            save_config(app);
            Task::none()
        }
        Message::SetQuickFilterTerm(val) => {
            app.ob_quick_filter_term_input = val.clone();
            app.quick_filter_term = val;
            save_config(app);
            refresh_filtered_tasks(app);
            Task::none()
        }
        Message::SetQuickFilterIcon(val) => {
            app.ob_quick_filter_icon_input = val.clone();
            app.quick_filter_icon = val;
            save_config(app);
            Task::none()
        }
        Message::DeleteAllCalendarEvents => {
            // Clone client first to avoid immutable borrow of `app` conflicting with mutable borrow below
            let client_opt = app.client.clone();

            if let Some(client) = client_opt {
                app.deleting_events = true;

                // Auto-disable event creation
                app.create_events_for_tasks = false;
                save_config(app);

                // Grab all known calendar hrefs
                let cals: Vec<String> = app.store.calendars.keys().cloned().collect();

                return Task::perform(async_delete_all_events_wrapper(client, cals), |res| {
                    Message::BackfillEventsComplete(res.map_err(|e| e.to_string()))
                });
            }
            Task::none()
        }
        Message::BackfillEventsComplete(Ok(count)) => {
            app.deleting_events = false;
            app.last_sync_failed = false;

            if count > 0 {
                let action = if app.create_events_for_tasks {
                    rust_i18n::t!("calendar_action_created")
                } else {
                    rust_i18n::t!("calendar_action_deleted")
                };

                let translated = if count == 1 {
                    rust_i18n::t!(
                        "calendar_events_changed.one",
                        action = action,
                        count = count
                    )
                    .to_string()
                } else {
                    rust_i18n::t!(
                        "calendar_events_changed.other",
                        action = action,
                        count = count
                    )
                    .to_string()
                };

                app.error_msg = Some(format!("✓ {}", translated));
            } else {
                app.error_msg = Some(rust_i18n::t!("no_events_changed").to_string());
            }
            Task::none()
        }
        Message::BackfillEventsComplete(Err(e)) => {
            app.deleting_events = false;
            app.last_sync_failed = true;
            app.error_msg = Some(format!("Backfill error: {}", e));
            Task::none()
        }
        Message::ExportLocalIcs(calendar_href) => {
            let tasks_result = LocalStorage::load_for_href(app.ctx.as_ref(), &calendar_href);
            let cal_name = calendar_href.clone();

            Task::perform(
                async move {
                    let tasks = tasks_result.map_err(|e| e.to_string())?;
                    let ics_content = LocalStorage::to_ics_string(&tasks);

                    let cal_id = cal_name.strip_prefix("local://").unwrap_or("backup");
                    let filename = format!("cfait_{}.ics", cal_id);

                    let file_handle = rfd::AsyncFileDialog::new()
                        .add_filter("Calendar", &["ics"])
                        .set_file_name(&filename)
                        .save_file()
                        .await;

                    if let Some(handle) = file_handle {
                        let path = handle.path().to_path_buf();
                        #[cfg(not(target_arch = "wasm32"))]
                        {
                            use tokio::io::AsyncWriteExt;
                            let mut file = tokio::fs::File::create(&path)
                                .await
                                .map_err(|e| e.to_string())?;
                            file.write_all(ics_content.as_bytes())
                                .await
                                .map_err(|e| e.to_string())?;
                        }
                        Ok(path)
                    } else {
                        Err(rust_i18n::t!("export_cancelled").to_string())
                    }
                },
                Message::ExportSaved,
            )
        }
        Message::ExportSaved(Ok(path)) => {
            app.error_msg = Some(format!(
                "Exported to: {:?}",
                path.file_name().unwrap_or_default()
            ));
            Task::none()
        }
        Message::ExportSaved(Err(e)) => {
            if e != rust_i18n::t!("export_cancelled") {
                app.error_msg = Some(rust_i18n::t!("export_error", error = e).to_string());
            }
            Task::none()
        }
        Message::ImportLocalIcs(calendar_href) => {
            let ctx = app.ctx.clone();
            Task::perform(
                async move {
                    if let Some(file) = rfd::AsyncFileDialog::new()
                        .add_filter("iCalendar", &["ics"])
                        .pick_file()
                        .await
                    {
                        let content = file.read().await;
                        match String::from_utf8(content) {
                            Ok(ics_content) => {
                                match LocalStorage::import_from_ics(
                                    ctx.as_ref(),
                                    &calendar_href,
                                    &ics_content,
                                ) {
                                    Ok(count) => {
                                        let file_name = file.file_name();
                                        let msg = if count == 1 {
                                            rust_i18n::t!(
                                                "import_success_from_file.one",
                                                file = file_name
                                            )
                                            .to_string()
                                        } else {
                                            rust_i18n::t!(
                                                "import_success_from_file.other",
                                                count = count,
                                                file = file_name
                                            )
                                            .to_string()
                                        };
                                        Ok(msg)
                                    }
                                    Err(e) => {
                                        Err(rust_i18n::t!("import_failed", error = e.to_string())
                                            .to_string())
                                    }
                                }
                            }
                            Err(e) => Err(rust_i18n::t!(
                                "import_failed_read_text",
                                error = e.to_string()
                            )
                            .to_string()),
                        }
                    } else {
                        Err(rust_i18n::t!("import_cancelled").to_string())
                    }
                },
                Message::ImportCompleted,
            )
        }
        Message::ImportCompleted(Ok(msg)) => {
            app.error_msg = Some(msg);
            if let Some(client) = &app.client {
                app.loading = true;
                return Task::perform(
                    async_fetch_all_wrapper(client.clone(), app.calendars.clone()),
                    |res| Message::RefreshedAll(res.map_err(|e| e.to_string())),
                );
            }
            Task::none()
        }
        Message::ImportCompleted(Err(e)) => {
            if e != rust_i18n::t!("import_cancelled") {
                app.error_msg = Some(e);
            }
            Task::none()
        }
        Message::AddLocalCalendar => {
            use uuid::Uuid;
            let id = Uuid::new_v4().to_string();
            let new_cal = crate::model::CalendarListEntry {
                name: rust_i18n::t!("new_calendar_name").to_string(),
                href: format!("local://{}", id),
                color: None,
            };
            app.local_cals_editing.push(new_cal.clone());

            let _ = LocalCalendarRegistry::save(app.ctx.as_ref(), &app.local_cals_editing);

            if !app.calendars.iter().any(|c| c.href == new_cal.href) {
                app.calendars.push(new_cal.clone());
            }
            app.sort_calendars();

            app.store.insert(new_cal.href.clone(), vec![]);

            refresh_filtered_tasks(app);
            Task::none()
        }
        Message::DeleteLocalCalendar(href) => {
            if href == LOCAL_CALENDAR_HREF {
                return Task::none();
            }

            if let Some(idx) = app.local_cals_editing.iter().position(|c| c.href == href) {
                app.local_cals_editing.remove(idx);
                let _ = LocalCalendarRegistry::save(app.ctx.as_ref(), &app.local_cals_editing);

                app.calendars.retain(|c| c.href != href);
                app.store.remove(&href);

                if let Some(path) = LocalStorage::get_path_for_href(app.ctx.as_ref(), &href) {
                    let _ = std::fs::remove_file(path);
                }

                refresh_filtered_tasks(app);
            }
            Task::none()
        }
        Message::LocalCalendarNameChanged(href, name) => {
            if let Some(cal) = app.local_cals_editing.iter_mut().find(|c| c.href == href) {
                cal.name = name.clone();
                let _ = LocalCalendarRegistry::save(app.ctx.as_ref(), &app.local_cals_editing);

                if let Some(main_cal) = app.calendars.iter_mut().find(|c| c.href == href) {
                    main_cal.name = name;
                }
            }
            Task::none()
        }
        Message::OpenColorPicker(href, current) => {
            app.color_picker_active_href = Some(href);
            app.temp_color = current;
            Task::none()
        }
        Message::CancelColorPicker => {
            app.color_picker_active_href = None;
            Task::none()
        }
        Message::SubmitColorPicker(color) => {
            if let Some(active_href) = &app.color_picker_active_href.clone() {
                if let Some(cal) = app
                    .local_cals_editing
                    .iter_mut()
                    .find(|c| c.href == *active_href)
                {
                    let r = (color.r * 255.0) as u8;
                    let g = (color.g * 255.0) as u8;
                    let b = (color.b * 255.0) as u8;
                    let hex = format!("#{:02X}{:02X}{:02X}", r, g, b);
                    cal.color = Some(hex.clone());
                    let _ = LocalCalendarRegistry::save(app.ctx.as_ref(), &app.local_cals_editing);

                    if let Some(main_cal) =
                        app.calendars.iter_mut().find(|c| c.href == *active_href)
                    {
                        main_cal.color = Some(hex.clone());
                    }
                }
                if let Some(cal) = app
                    .remote_cals_editing
                    .iter_mut()
                    .find(|c| c.href == *active_href)
                {
                    let r = (color.r * 255.0) as u8;
                    let g = (color.g * 255.0) as u8;
                    let b = (color.b * 255.0) as u8;
                    let hex = format!("#{:02X}{:02X}{:02X}", r, g, b);
                    cal.color = Some(hex);
                }
            }
            app.color_picker_active_href = None;
            Task::none()
        }
        Message::AddRemoteCalendar => {
            use uuid::Uuid;
            let id = Uuid::new_v4().to_string();
            let new_cal = crate::model::CalendarListEntry {
                name: rust_i18n::t!("new_calendar_name").to_string(),
                href: format!("new_remote_{}", id),
                color: None,
            };
            app.remote_cals_editing.push(new_cal);
            Task::none()
        }
        Message::RemoteCalendarNameChanged(href, name) => {
            if let Some(cal) = app.remote_cals_editing.iter_mut().find(|c| c.href == href) {
                cal.name = name;
            }
            Task::none()
        }
        Message::SubmitRemoteCalendar(href) => {
            if let Some(cal) = app.remote_cals_editing.iter().find(|c| c.href == href)
                && let Some(client) = &app.client
            {
                let name = cal.name.clone();
                let color = cal.color.clone();
                let old_href = href.clone();
                app.loading = true;
                if href.starts_with("new_remote_") {
                    return Task::perform(
                        async_create_remote_calendar_wrapper(client.clone(), name, color),
                        move |res| {
                            Message::RemoteCalendarCreated(
                                old_href.clone(),
                                res.map_err(|e| e.to_string()),
                            )
                        },
                    );
                } else {
                    let h = href.clone();
                    return Task::perform(
                        async_update_remote_calendar_wrapper(client.clone(), h, name, color),
                        move |res| {
                            Message::RemoteCalendarUpdated(
                                old_href.clone(),
                                res.map_err(|e| e.to_string()),
                            )
                        },
                    );
                }
            }
            Task::none()
        }
        Message::RemoteCalendarCreated(old_href, Ok(new_href)) => {
            app.loading = false;
            app.error_msg = Some(rust_i18n::t!("collection_created").to_string());
            if let Some(cal) = app
                .remote_cals_editing
                .iter_mut()
                .find(|c| c.href == old_href)
            {
                cal.href = new_href.clone();
                app.calendars.push(cal.clone()); // Optimistic update
            }
            app.sort_calendars();
            Task::perform(async { Ok::<(), String>(()) }, |_| Message::Refresh)
        }
        Message::RemoteCalendarUpdated(href, Ok(_)) => {
            app.loading = false;
            app.error_msg = Some(rust_i18n::t!("collection_updated").to_string());
            if let Some(cal) = app.remote_cals_editing.iter().find(|c| c.href == href)
                && let Some(main_cal) = app.calendars.iter_mut().find(|c| c.href == href)
            {
                main_cal.name = cal.name.clone();
                main_cal.color = cal.color.clone();
            }
            Task::perform(async { Ok::<(), String>(()) }, |_| Message::Refresh)
        }
        Message::RemoteCalendarCreated(_, Err(e)) | Message::RemoteCalendarUpdated(_, Err(e)) => {
            app.loading = false;
            app.error_msg = Some(e);
            Task::none()
        }

        Message::IcsFileLoaded(Ok((file_path, content))) => {
            let task_count = content.split("BEGIN:VTODO").count().saturating_sub(1);

            app.ics_import_dialog_open = true;
            app.ics_import_file_path = Some(file_path);
            app.ics_import_content = Some(content);
            app.ics_import_task_count = Some(task_count);
            app.ics_import_selected_calendar = app
                .calendars
                .iter()
                .filter(|c| !app.disabled_calendars.contains(&c.href))
                .map(|c| c.href.clone())
                .next();

            Task::none()
        }
        Message::IcsFileLoaded(Err(e)) => {
            app.error_msg = Some(format!(
                "{}",
                rust_i18n::t!("import_failed_read_text", error = e)
            ));
            Task::none()
        }
        Message::IcsImportDialogCalendarSelected(href) => {
            app.ics_import_selected_calendar = Some(href);
            Task::none()
        }
        Message::IcsImportDialogCancel => {
            app.ics_import_dialog_open = false;
            app.ics_import_file_path = None;
            app.ics_import_content = None;
            app.ics_import_selected_calendar = None;
            app.ics_import_task_count = None;
            Task::none()
        }
        Message::IcsImportDialogConfirm => {
            if let Some(calendar_href) = &app.ics_import_selected_calendar.clone()
                && let Some(ics_content) = &app.ics_import_content.clone()
            {
                app.ics_import_dialog_open = false;
                let file_path = app.ics_import_file_path.take();
                app.ics_import_content = None;
                app.ics_import_selected_calendar = None;
                app.ics_import_task_count = None;

                let href = calendar_href.clone();
                let content = ics_content.clone();
                let ctx = app.ctx.clone();
                return Task::perform(
                    async move {
                        match LocalStorage::import_from_ics(ctx.as_ref(), &href, &content) {
                            Ok(count) => {
                                let file_name = file_path
                                    .as_ref()
                                    .and_then(|p| std::path::Path::new(p).file_name())
                                    .and_then(|n| n.to_str())
                                    .unwrap_or("file");
                                let msg = if count == 1 {
                                    rust_i18n::t!("import_success_from_file.one", file = file_name)
                                        .to_string()
                                } else {
                                    rust_i18n::t!(
                                        "import_success_from_file.other",
                                        count = count,
                                        file = file_name
                                    )
                                    .to_string()
                                };
                                Ok(msg)
                            }
                            Err(e) => Err(format!("{}", rust_i18n::t!("import_failed", error = e))),
                        }
                    },
                    Message::ImportCompleted,
                );
            }
            Task::none()
        }

        _ => Task::none(),
    }
}
