use super::*;

impl BdsApp {
    pub(super) fn handle_post_editor_msg(&mut self, msg: PostEditorMsg) -> Task<Message> {
        enum DeferredPostAction {
            None,
            SyncEmbeddedPreview,
            Analyze(String),
            AnalyzeTaxonomy(String),
            EnsureTag {
                post_id: String,
                tag: String,
            },
            EnsureCategory {
                post_id: String,
                category: String,
            },
            LoadSemanticTags(String),
            AddGalleryImages(String),
            DetectLanguage(String),
            OpenTranslate(String),
            TranslateTo {
                post_id: String,
                target_language: String,
            },
            Save(String),
            Publish(String),
            Archive(String),
            Unarchive(String),
            Discard(String),
            ShowDelete {
                tab_id: String,
                name: String,
            },
            OpenInsertLink(String),
            OpenInsertMedia {
                post_id: String,
                link_only: bool,
            },
            OpenGallery(String),
            OpenLinkedMedia(String),
            UnlinkLinkedMedia {
                post_id: String,
                media_id: String,
            },
            InsertSelectedLink {
                post_id: String,
                linked_post_id: String,
            },
            CreateLinkedPost(String),
            InsertSelectedMedia {
                post_id: String,
                media_id: String,
            },
            SetLinkTab(modal::PostInsertLinkTab),
            SetLinkSearch(String),
            SetExternalUrl(String),
            SetExternalText(String),
            InsertExternalLink,
            SetMediaSearch(String),
            SelectGalleryImage(usize),
            GalleryPrevious,
            GalleryNext,
            GalleryCloseLightbox,
        }

        let mut deferred = DeferredPostAction::None;
        let mut refresh_linked_media: Option<(String, String)> = None;
        if let Some(tab_id) = self.active_tab.clone()
            && let Some(state) = self.post_editors.get_mut(&tab_id)
        {
            match msg {
                PostEditorMsg::ToggleQuickActions => {
                    state.quick_actions_open = !state.quick_actions_open;
                }
                PostEditorMsg::AnalyzeWithAi => {
                    state.quick_actions_open = false;
                    deferred = DeferredPostAction::Analyze(tab_id.clone());
                }
                PostEditorMsg::AnalyzeTaxonomy => {
                    state.quick_actions_open = false;
                    deferred = DeferredPostAction::AnalyzeTaxonomy(tab_id.clone());
                }
                PostEditorMsg::AddGalleryImages => {
                    state.quick_actions_open = false;
                    deferred = DeferredPostAction::AddGalleryImages(state.post_id.clone());
                }
                PostEditorMsg::SwitchEditorMode(mode) => {
                    state.set_editor_mode(&mode);
                    deferred = DeferredPostAction::SyncEmbeddedPreview;
                }
                PostEditorMsg::DetectLanguage => {
                    state.quick_actions_open = false;
                    deferred = DeferredPostAction::DetectLanguage(tab_id.clone());
                }
                PostEditorMsg::Translate => {
                    state.quick_actions_open = false;
                    deferred = DeferredPostAction::OpenTranslate(tab_id.clone());
                }
                PostEditorMsg::TranslateTo(target_language) => {
                    deferred = DeferredPostAction::TranslateTo {
                        post_id: tab_id.clone(),
                        target_language,
                    };
                }
                PostEditorMsg::TitleChanged(s) => {
                    state.title = s;
                    state.mark_dirty();
                }
                PostEditorMsg::SlugChanged(s) => {
                    state.slug = s;
                    state.mark_dirty();
                }
                PostEditorMsg::ExcerptChanged(s) => {
                    state.excerpt = s;
                    state.mark_dirty();
                }
                PostEditorMsg::ContentChanged(new_text) => {
                    state.content = new_text;
                    state.mark_dirty();
                    refresh_linked_media = Some((state.post_id.clone(), state.content.clone()));
                }
                PostEditorMsg::AuthorChanged(s) => {
                    state.author = s;
                    state.mark_dirty();
                }
                PostEditorMsg::LanguageChanged(s) => {
                    state.language = s;
                    state.mark_dirty();
                }
                PostEditorMsg::TemplateSlugChanged(s) => {
                    state.template_slug = s;
                    state.mark_dirty();
                }
                PostEditorMsg::ToggleDoNotTranslate(b) => {
                    state.do_not_translate = b;
                    state.mark_dirty();
                }
                PostEditorMsg::ToggleMetadata => {
                    state.metadata_expanded = !state.metadata_expanded;
                    if state.metadata_expanded {
                        deferred = DeferredPostAction::LoadSemanticTags(state.post_id.clone());
                    }
                }
                PostEditorMsg::ToggleExcerpt => {
                    state.excerpt_expanded = !state.excerpt_expanded;
                }
                PostEditorMsg::SwitchLanguage(lang) => {
                    state.switch_language(&lang);
                    if state.editor_mode == "preview" {
                        deferred = DeferredPostAction::SyncEmbeddedPreview;
                    }
                }
                PostEditorMsg::TagsInputChanged(s) => {
                    state.tags_input = s;
                    if state.tags_input.trim().is_empty() {
                        deferred = DeferredPostAction::LoadSemanticTags(state.post_id.clone());
                    }
                }
                PostEditorMsg::TagsInputSubmit => {
                    let tag = state.tags_input.trim().to_string();
                    if !tag.is_empty()
                        && !state
                            .tags
                            .iter()
                            .any(|current| current.eq_ignore_ascii_case(&tag))
                    {
                        state.tags.push(tag.clone());
                        state.mark_dirty();
                        deferred = DeferredPostAction::EnsureTag {
                            post_id: state.post_id.clone(),
                            tag,
                        };
                    }
                    state.tags_input.clear();
                }
                PostEditorMsg::AddSuggestedTag(tag) => {
                    if !state
                        .tags
                        .iter()
                        .any(|current| current.eq_ignore_ascii_case(&tag))
                    {
                        state.tags.push(tag.clone());
                        state.mark_dirty();
                        deferred = DeferredPostAction::EnsureTag {
                            post_id: state.post_id.clone(),
                            tag: tag.clone(),
                        };
                    }
                    state.tags_input.clear();
                }
                PostEditorMsg::RemoveTag(tag) => {
                    state.tags.retain(|t| t != &tag);
                    state.mark_dirty();
                }
                PostEditorMsg::CategoriesInputChanged(s) => {
                    state.categories_input = s;
                }
                PostEditorMsg::CategoriesInputSubmit => {
                    let cat = state.categories_input.trim().to_string();
                    if !cat.is_empty()
                        && !state
                            .categories
                            .iter()
                            .any(|current| current.eq_ignore_ascii_case(&cat))
                    {
                        state.categories.push(cat.clone());
                        state.mark_dirty();
                        deferred = DeferredPostAction::EnsureCategory {
                            post_id: state.post_id.clone(),
                            category: cat,
                        };
                    }
                    state.categories_input.clear();
                }
                PostEditorMsg::AddSuggestedCategory(category) => {
                    if !state
                        .categories
                        .iter()
                        .any(|current| current.eq_ignore_ascii_case(&category))
                    {
                        state.categories.push(category.clone());
                        state.mark_dirty();
                        deferred = DeferredPostAction::EnsureCategory {
                            post_id: state.post_id.clone(),
                            category,
                        };
                    }
                    state.categories_input.clear();
                }
                PostEditorMsg::RemoveCategory(cat) => {
                    state.categories.retain(|c| c != &cat);
                    state.mark_dirty();
                }
                PostEditorMsg::Save => {
                    deferred = DeferredPostAction::Save(tab_id.clone());
                }
                PostEditorMsg::Publish => {
                    deferred = DeferredPostAction::Publish(tab_id.clone());
                }
                PostEditorMsg::Archive => {
                    state.quick_actions_open = false;
                    deferred = DeferredPostAction::Archive(tab_id.clone());
                }
                PostEditorMsg::Unarchive => {
                    state.quick_actions_open = false;
                    deferred = DeferredPostAction::Unarchive(tab_id.clone());
                }
                PostEditorMsg::Discard => {
                    deferred = DeferredPostAction::Discard(tab_id.clone());
                }
                PostEditorMsg::Delete => {
                    deferred = DeferredPostAction::ShowDelete {
                        tab_id: tab_id.clone(),
                        name: state.title.clone(),
                    };
                }
                PostEditorMsg::InsertLink => {
                    deferred = DeferredPostAction::OpenInsertLink(state.post_id.clone());
                }
                PostEditorMsg::InsertMedia => {
                    deferred = DeferredPostAction::OpenInsertMedia {
                        post_id: state.post_id.clone(),
                        link_only: false,
                    };
                }
                PostEditorMsg::Gallery => {
                    deferred = DeferredPostAction::OpenGallery(state.post_id.clone());
                }
                PostEditorMsg::LinkExistingMedia => {
                    deferred = DeferredPostAction::OpenInsertMedia {
                        post_id: state.post_id.clone(),
                        link_only: true,
                    };
                }
                PostEditorMsg::OpenLinkedMedia(media_id) => {
                    deferred = DeferredPostAction::OpenLinkedMedia(media_id);
                }
                PostEditorMsg::UnlinkLinkedMedia(media_id) => {
                    deferred = DeferredPostAction::UnlinkLinkedMedia {
                        post_id: state.post_id.clone(),
                        media_id,
                    };
                }
                PostEditorMsg::PostInsertLinkSelected(linked_post_id) => {
                    deferred = DeferredPostAction::InsertSelectedLink {
                        post_id: state.post_id.clone(),
                        linked_post_id,
                    };
                }
                PostEditorMsg::PostInsertLinkCreate => {
                    deferred = DeferredPostAction::CreateLinkedPost(state.post_id.clone());
                }
                PostEditorMsg::PostInsertMediaSelected(media_id) => {
                    deferred = DeferredPostAction::InsertSelectedMedia {
                        post_id: state.post_id.clone(),
                        media_id,
                    };
                }
                PostEditorMsg::PostGalleryImageSelected(index) => {
                    deferred = DeferredPostAction::SelectGalleryImage(index);
                }
                PostEditorMsg::PostInsertLinkTabSwitch(tab) => {
                    deferred = DeferredPostAction::SetLinkTab(tab);
                }
                PostEditorMsg::PostInsertLinkSearch(query) => {
                    deferred = DeferredPostAction::SetLinkSearch(query);
                }
                PostEditorMsg::PostInsertLinkUrlChanged(url) => {
                    deferred = DeferredPostAction::SetExternalUrl(url);
                }
                PostEditorMsg::PostInsertLinkTextChanged(text) => {
                    deferred = DeferredPostAction::SetExternalText(text);
                }
                PostEditorMsg::PostInsertLinkExternalInsert => {
                    deferred = DeferredPostAction::InsertExternalLink;
                }
                PostEditorMsg::PostInsertMediaSearch(query) => {
                    deferred = DeferredPostAction::SetMediaSearch(query);
                }
                PostEditorMsg::PostGalleryPrevious => {
                    deferred = DeferredPostAction::GalleryPrevious;
                }
                PostEditorMsg::PostGalleryNext => {
                    deferred = DeferredPostAction::GalleryNext;
                }
                PostEditorMsg::PostGalleryCloseLightbox => {
                    deferred = DeferredPostAction::GalleryCloseLightbox;
                }
            }

            if let Some(tab) = self.tabs.iter_mut().find(|t| t.id == state.post_id) {
                tab.is_dirty = state.is_dirty;
            }
        }

        if let Some((post_id, content)) = refresh_linked_media {
            let linked_media = self.load_post_media_items(&post_id, Some(&content));
            if let Some(state) = self.post_editors.get_mut(&post_id) {
                state.linked_media = linked_media;
            }
        }

        match deferred {
            DeferredPostAction::None => Task::none(),
            DeferredPostAction::SyncEmbeddedPreview => self.sync_embedded_preview_for_active_post(),
            DeferredPostAction::Analyze(tab_id) => self.run_post_ai_analysis(&tab_id),
            DeferredPostAction::AnalyzeTaxonomy(tab_id) => self.run_post_taxonomy_analysis(&tab_id),
            DeferredPostAction::EnsureTag { post_id, tag } => {
                self.ensure_post_editor_tag(&post_id, &tag)
            }
            DeferredPostAction::EnsureCategory { post_id, category } => {
                self.ensure_post_editor_category(&post_id, &category)
            }
            DeferredPostAction::LoadSemanticTags(post_id) => {
                Task::done(Message::LoadSemanticTagSuggestions(post_id))
            }
            DeferredPostAction::AddGalleryImages(post_id) => self.add_gallery_images(&post_id),
            DeferredPostAction::DetectLanguage(tab_id) => self.detect_post_language(&tab_id),
            DeferredPostAction::OpenTranslate(tab_id) => self.open_post_translation_modal(&tab_id),
            DeferredPostAction::TranslateTo {
                post_id,
                target_language,
            } => self.translate_post_to(&post_id, &target_language),
            DeferredPostAction::Save(tab_id) => self.save_post_editor(&tab_id, true),
            DeferredPostAction::Publish(tab_id) => self.publish_post_editor(&tab_id),
            DeferredPostAction::Archive(tab_id) => self.archive_post_editor(&tab_id),
            DeferredPostAction::Unarchive(tab_id) => self.unarchive_post_editor(&tab_id),
            DeferredPostAction::Discard(tab_id) => self.discard_post_editor(&tab_id),
            DeferredPostAction::ShowDelete { tab_id, name } => {
                Task::done(Message::ShowModal(modal::ModalState::ConfirmDelete {
                    entity_name: name,
                    references: Vec::new(),
                    on_confirm: modal::ConfirmAction::DeletePost(tab_id),
                }))
            }
            DeferredPostAction::OpenInsertLink(post_id) => self.insert_link_modal(&post_id),
            DeferredPostAction::OpenInsertMedia { post_id, link_only } => {
                self.insert_media_modal(&post_id, link_only)
            }
            DeferredPostAction::OpenGallery(post_id) => self.post_gallery(&post_id),
            DeferredPostAction::OpenLinkedMedia(media_id) => {
                let title = self
                    .db
                    .as_ref()
                    .and_then(|db| {
                        bds_core::db::queries::media::get_media_by_id(db.conn(), &media_id)
                            .ok()
                            .map(|media| media.title.unwrap_or(media.original_name))
                    })
                    .unwrap_or_else(|| media_id.clone());
                Task::done(Message::OpenTab(Tab {
                    id: media_id,
                    tab_type: TabType::Media,
                    title,
                    is_transient: false,
                    is_dirty: false,
                }))
            }
            DeferredPostAction::UnlinkLinkedMedia { post_id, media_id } => {
                if let (Some(db), Some(data_dir)) = (&self.db, &self.data_dir) {
                    if let Err(err) = engine::post_media::unlink_media_from_post(
                        db.conn(),
                        data_dir,
                        &post_id,
                        &media_id,
                    ) {
                        self.notify_operation_failed("editor.unlinkMedia", err);
                        return Task::none();
                    }
                    self.refresh_post_relationships(&post_id);
                }
                Task::none()
            }
            DeferredPostAction::InsertSelectedLink {
                post_id,
                linked_post_id,
            } => self.insert_selected_post_link(&post_id, &linked_post_id),
            DeferredPostAction::CreateLinkedPost(post_id) => {
                self.insert_created_post_link(&post_id)
            }
            DeferredPostAction::InsertSelectedMedia { post_id, media_id } => {
                self.insert_selected_media(&post_id, &media_id)
            }
            DeferredPostAction::SetLinkTab(tab) => {
                self.refresh_post_insert_link_modal(Some(tab), None, None, None);
                Task::none()
            }
            DeferredPostAction::SetLinkSearch(query) => {
                self.refresh_post_insert_link_modal(None, Some(query), None, None);
                Task::none()
            }
            DeferredPostAction::SetExternalUrl(url) => {
                self.refresh_post_insert_link_modal(None, None, Some(url), None);
                Task::none()
            }
            DeferredPostAction::SetExternalText(text) => {
                self.refresh_post_insert_link_modal(None, None, None, Some(text));
                Task::none()
            }
            DeferredPostAction::InsertExternalLink => {
                if let Some(modal::ModalState::PostInsertLink {
                    post_id,
                    external_url,
                    external_text,
                    ..
                }) = self.active_modal.clone()
                {
                    if let Some(markdown) =
                        modal::external_link_markdown(&external_url, &external_text)
                    {
                        self.insert_markdown_into_post(&post_id, &markdown)
                    } else {
                        self.notify(
                            ToastLevel::Error,
                            &t(self.ui_locale, "modal.postInsertLink.urlRequired"),
                        );
                        Task::none()
                    }
                } else {
                    Task::none()
                }
            }
            DeferredPostAction::SetMediaSearch(query) => {
                self.refresh_insert_media_modal(query);
                Task::none()
            }
            DeferredPostAction::SelectGalleryImage(index) => {
                self.update_gallery_selection(Some(index));
                Task::none()
            }
            DeferredPostAction::GalleryPrevious => {
                self.step_gallery_selection(-1);
                Task::none()
            }
            DeferredPostAction::GalleryNext => {
                self.step_gallery_selection(1);
                Task::none()
            }
            DeferredPostAction::GalleryCloseLightbox => {
                self.update_gallery_selection(None);
                Task::none()
            }
        }
    }

    pub(super) fn handle_media_editor_msg(&mut self, msg: MediaEditorMsg) -> Task<Message> {
        enum DeferredMediaAction {
            None,
            Analyze(String),
            DetectLanguage(String),
            OpenTranslate(String),
            TranslateTo {
                media_id: String,
                target_language: String,
            },
            LinkPost {
                media_id: String,
                post_id: String,
            },
            OpenLinkedPost(String),
            UnlinkPost {
                media_id: String,
                post_id: String,
            },
            Save(String),
            Replace(String),
            Delete {
                tab_id: String,
                name: String,
            },
        }

        let mut deferred = DeferredMediaAction::None;
        let mut picker_refresh: Option<(String, String)> = None;
        if let Some(tab_id) = self.active_tab.clone()
            && let Some(state) = self.media_editors.get_mut(&tab_id)
        {
            match msg {
                MediaEditorMsg::ToggleQuickActions => {
                    state.quick_actions_open = !state.quick_actions_open;
                }
                MediaEditorMsg::AnalyzeWithAi => {
                    state.quick_actions_open = false;
                    deferred = DeferredMediaAction::Analyze(tab_id.clone());
                }
                MediaEditorMsg::DetectLanguage => {
                    state.quick_actions_open = false;
                    deferred = DeferredMediaAction::DetectLanguage(tab_id.clone());
                }
                MediaEditorMsg::TranslateMetadata => {
                    state.quick_actions_open = false;
                    deferred = DeferredMediaAction::OpenTranslate(tab_id.clone());
                }
                MediaEditorMsg::TranslateTo(target_language) => {
                    deferred = DeferredMediaAction::TranslateTo {
                        media_id: tab_id.clone(),
                        target_language,
                    };
                }
                MediaEditorMsg::TitleChanged(s) => {
                    state.title = s;
                    state.is_dirty = true;
                }
                MediaEditorMsg::AltChanged(s) => {
                    state.alt = s;
                    state.is_dirty = true;
                }
                MediaEditorMsg::CaptionChanged(s) => {
                    state.caption = s;
                    state.is_dirty = true;
                }
                MediaEditorMsg::AuthorChanged(s) => {
                    state.author = s;
                    state.is_dirty = true;
                }
                MediaEditorMsg::LanguageChanged(s) => {
                    state.language = s;
                    state.is_dirty = true;
                }
                MediaEditorMsg::TagsChanged(s) => {
                    state.tags_input = s;
                    state.is_dirty = true;
                }
                MediaEditorMsg::SwitchLanguage(lang) => {
                    state.switch_language(&lang);
                }
                MediaEditorMsg::TogglePostPicker => {
                    let next_open = !state.post_picker_open;
                    state.post_picker_open = next_open;
                    if !next_open {
                        state.post_picker_results.clear();
                    } else {
                        picker_refresh =
                            Some((state.media_id.clone(), state.post_picker_search.clone()));
                    }
                }
                MediaEditorMsg::PostPickerSearchChanged(search) => {
                    state.post_picker_search = search;
                    if state.post_picker_open {
                        picker_refresh =
                            Some((state.media_id.clone(), state.post_picker_search.clone()));
                    }
                }
                MediaEditorMsg::LinkPost(post_id) => {
                    deferred = DeferredMediaAction::LinkPost {
                        media_id: state.media_id.clone(),
                        post_id,
                    };
                }
                MediaEditorMsg::OpenLinkedPost(post_id) => {
                    deferred = DeferredMediaAction::OpenLinkedPost(post_id);
                }
                MediaEditorMsg::UnlinkPost(post_id) => {
                    deferred = DeferredMediaAction::UnlinkPost {
                        media_id: state.media_id.clone(),
                        post_id,
                    };
                }
                MediaEditorMsg::Save => {
                    deferred = DeferredMediaAction::Save(tab_id.clone());
                }
                MediaEditorMsg::ReplaceFile => {
                    deferred = DeferredMediaAction::Replace(tab_id.clone());
                }
                MediaEditorMsg::Delete => {
                    deferred = DeferredMediaAction::Delete {
                        tab_id: tab_id.clone(),
                        name: state.title.clone(),
                    };
                }
            }
            if let Some(tab) = self
                .tabs
                .iter_mut()
                .find(|t| t.id == *state.media_id.as_str())
            {
                tab.is_dirty = state.is_dirty;
            }
        }
        if let Some((media_id, search)) = picker_refresh {
            let results = self.query_media_post_picker_results(&media_id, &search);
            if let Some(state) = self.media_editors.get_mut(&media_id) {
                state.post_picker_results = results;
            }
        }
        match deferred {
            DeferredMediaAction::None => Task::none(),
            DeferredMediaAction::Analyze(media_id) => self.run_media_ai_analysis(&media_id),
            DeferredMediaAction::DetectLanguage(media_id) => self.detect_media_language(&media_id),
            DeferredMediaAction::OpenTranslate(media_id) => {
                self.open_media_translation_modal(&media_id)
            }
            DeferredMediaAction::TranslateTo {
                media_id,
                target_language,
            } => self.translate_media_to(&media_id, &target_language),
            DeferredMediaAction::LinkPost { media_id, post_id } => {
                self.link_media_to_post(&media_id, &post_id)
            }
            DeferredMediaAction::OpenLinkedPost(post_id) => {
                let title = self
                    .db
                    .as_ref()
                    .and_then(|db| {
                        bds_core::db::queries::post::get_post_by_id(db.conn(), &post_id).ok()
                    })
                    .map(|post| post.title)
                    .unwrap_or_else(|| post_id.clone());
                Task::done(Message::OpenTab(Tab {
                    id: post_id,
                    tab_type: TabType::Post,
                    title,
                    is_transient: false,
                    is_dirty: false,
                }))
            }
            DeferredMediaAction::UnlinkPost { media_id, post_id } => {
                self.unlink_media_from_post(&media_id, &post_id)
            }
            DeferredMediaAction::Save(tab_id) => self.save_media_editor(&tab_id),
            DeferredMediaAction::Replace(media_id) => {
                crate::platform::dialog::pick_media_replacement(
                    media_id,
                    t(self.ui_locale, "dialog.replaceMedia"),
                    t(self.ui_locale, "dialog.imageFilter"),
                )
            }
            DeferredMediaAction::Delete { tab_id, name } => {
                Task::done(Message::ShowModal(modal::ModalState::ConfirmDelete {
                    entity_name: name,
                    references: Vec::new(),
                    on_confirm: modal::ConfirmAction::DeleteMedia(tab_id),
                }))
            }
        }
    }

    pub(super) fn handle_template_editor_msg(&mut self, msg: TemplateEditorMsg) -> Task<Message> {
        if let Some(tab_id) = self.active_tab.clone()
            && let Some(state) = self.template_editors.get_mut(&tab_id)
        {
            match msg {
                TemplateEditorMsg::TitleChanged(s) => {
                    state.title = s;
                    state.is_dirty = true;
                }
                TemplateEditorMsg::SlugChanged(s) => {
                    state.slug = s;
                    state.is_dirty = true;
                }
                TemplateEditorMsg::KindChanged(k) => {
                    state.kind = k.0;
                    state.is_dirty = true;
                }
                TemplateEditorMsg::EnabledChanged(b) => {
                    state.enabled = b;
                    state.is_dirty = true;
                }
                TemplateEditorMsg::ContentChanged(new_text) => {
                    state.content = new_text;
                    state.is_dirty = true;
                }
                TemplateEditorMsg::Save => {
                    return self.save_template_editor(&tab_id);
                }
                TemplateEditorMsg::Validate => {
                    if let Some(st) = self.template_editors.get_mut(&tab_id) {
                        match engine::template::validate_template(&st.content) {
                            Ok(()) => {
                                st.validation_error = None;
                            }
                            Err(e) => {
                                st.validation_error = Some(e);
                            }
                        }
                    }
                }
                TemplateEditorMsg::Delete => {
                    return self.show_template_delete_confirmation(&tab_id);
                }
            }
            if let Some(st) = self.template_editors.get(&tab_id)
                && let Some(tab) = self.tabs.iter_mut().find(|t| t.id == tab_id)
            {
                tab.is_dirty = st.is_dirty;
            }
        }
        Task::none()
    }

    pub(super) fn handle_script_editor_msg(&mut self, msg: ScriptEditorMsg) -> Task<Message> {
        let mut run_request = None;
        let mut syntax_notification = None;
        if let Some(tab_id) = self.active_tab.clone()
            && let Some(state) = self.script_editors.get_mut(&tab_id)
        {
            match msg {
                ScriptEditorMsg::TitleChanged(s) => {
                    state.title = s;
                    state.is_dirty = true;
                }
                ScriptEditorMsg::SlugChanged(s) => {
                    state.slug = s;
                    state.is_dirty = true;
                }
                ScriptEditorMsg::KindChanged(k) => {
                    state.kind = k.0;
                    state.is_dirty = true;
                }
                ScriptEditorMsg::EntrypointChanged(s) => {
                    state.entrypoint = s;
                    state.is_dirty = true;
                }
                ScriptEditorMsg::EnabledChanged(b) => {
                    state.enabled = b;
                    state.is_dirty = true;
                }
                ScriptEditorMsg::ContentChanged(new_text) => {
                    state.discovered_entrypoints = engine::script::discover_entrypoints(&new_text);
                    state.content = new_text;
                    state.is_dirty = true;
                }
                ScriptEditorMsg::Save => {
                    return self.save_script_editor(&tab_id);
                }
                ScriptEditorMsg::CheckSyntax => {
                    match engine::script::validate_script_syntax(&state.content) {
                        Ok(()) => {
                            state.validation_error = None;
                            syntax_notification = Some((
                                ToastLevel::Success,
                                t(self.ui_locale, "editor.syntaxValid"),
                            ));
                        }
                        Err(error) => {
                            state.validation_error = Some(error.clone());
                            syntax_notification = Some((
                                ToastLevel::Error,
                                tw(self.ui_locale, "editor.syntaxInvalid", &[("error", &error)]),
                            ));
                        }
                    }
                }
                ScriptEditorMsg::Run => {
                    run_request = Some((
                        state.content.clone(),
                        state.entrypoint.clone(),
                        state.kind.clone(),
                    ));
                }
                ScriptEditorMsg::Delete => {
                    let name = state.title.clone();
                    return Task::done(Message::ShowModal(modal::ModalState::ConfirmDelete {
                        entity_name: name,
                        references: Vec::new(),
                        on_confirm: modal::ConfirmAction::DeleteScript(tab_id),
                    }));
                }
            }
            if let Some(st) = self.script_editors.get(&tab_id)
                && let Some(tab) = self.tabs.iter_mut().find(|t| t.id == tab_id)
            {
                tab.is_dirty = st.is_dirty;
            }
        }
        if let Some((level, message)) = syntax_notification {
            self.notify(level, &message);
        }
        if let Some((source, entrypoint, kind)) = run_request {
            let offline_mode = self.offline_mode;
            let app_handler = crate::platform::script_host::handler(
                Arc::clone(&self.script_menu_actions),
                t(self.ui_locale, "dialog.selectFolder"),
            );
            return self.spawn_engine_task(
                "engine.runScript",
                move |db_path, project_id, data_dir, task_manager, task_id| {
                    let execution_kind = match kind {
                        bds_core::model::ScriptKind::Macro => {
                            bds_core::scripting::ExecutionKind::Macro
                        }
                        bds_core::model::ScriptKind::Utility => {
                            bds_core::scripting::ExecutionKind::Utility
                        }
                        bds_core::model::ScriptKind::Transform => {
                            bds_core::scripting::ExecutionKind::Transform
                        }
                    };
                    let host = bds_core::scripting::CoreHost::new(db_path, project_id, data_dir)
                        .with_task(Arc::clone(&task_manager), task_id)
                        .with_offline_mode(offline_mode)
                        .with_app_handler(Arc::clone(&app_handler));
                    let control = task_manager
                        .cancellation_flag(task_id)
                        .map(bds_core::scripting::ExecutionControl::from_cancelled)
                        .unwrap_or_default();
                    let result = bds_core::scripting::execute_with_host(
                        &source,
                        &entrypoint,
                        &serde_json::json!({}),
                        execution_kind,
                        &control,
                        Arc::new(host),
                    )?;
                    let mut lines = result.output;
                    lines.extend(
                        result
                            .toasts
                            .into_iter()
                            .map(|message| format!("toast: {message}")),
                    );
                    if result.value != serde_json::Value::Null {
                        lines.push(result.value.to_string());
                    }
                    Ok(if lines.is_empty() {
                        "completed".to_string()
                    } else {
                        lines.join("\n")
                    })
                },
            );
        }
        Task::none()
    }
}
