use crate::message_handler::arrow_keys::InstSelectOperation;
use crate::message_update::MSG_RESIZE;
use crate::state::{
    AutoSaveKind, CreateInstanceMessage, InfoMessage, LaunchMessage, LaunchTab, Launcher,
    LauncherSettingsMessage, LauncherSettingsTab, MainMenuMessage, ManageModsMessage,
    MenuCreateInstance, MenuCreateInstanceChoosing, MenuEditMods, MenuEditPresets,
    MenuExportInstance, MenuInstallFabric, MenuInstallOptifine, MenuInstallPaper,
    MenuLoginAlternate, MenuLoginMS, MenuRecommendedMods, MenuWelcome, Message, State,
};
use iced::{
    Task,
    keyboard::{self, Key, key::Named},
};
use ql_core::{
    Instance, err,
    jarmod::{JarMod, JarMods},
    pt,
};
use std::ffi::OsStr;
use std::path::Path;

impl Launcher {
    pub fn iced_event(&mut self, event: iced::Event, status: iced::event::Status) -> Task<Message> {
        match event {
            iced::Event::Window(event) => match event {
                iced::window::Event::CloseRequested => {
                    pt!(no_log, "Closing...");
                    self.close_launcher();
                }
                iced::window::Event::Resized(size) => {
                    self.window_state.size = (size.width, size.height);

                    // Remember window height
                    let window = self.config.window.get_or_insert_default();
                    window.width = Some(size.width);
                    window.height = Some(size.height);
                    if window.save_window_size {
                        self.autosave.remove(&AutoSaveKind::LauncherConfig);
                    }

                    // Clear the "Resize the window to apply changes"
                    // after changing UI scale
                    if let State::GenericMessage(msg) = &self.state {
                        if msg == MSG_RESIZE {
                            return self.update(
                                LauncherSettingsMessage::Open(LauncherSettingsTab::UserInterface)
                                    .into(),
                            );
                        }
                    }
                }
                iced::window::Event::FileHovered(_) => {
                    self.set_drag_and_drop_hover(true);
                }
                iced::window::Event::FilesHoveredLeft => {
                    self.set_drag_and_drop_hover(false);
                }
                iced::window::Event::FileDropped(path) => {
                    self.set_drag_and_drop_hover(false);

                    if let (Some(extension), Some(filename)) = (
                        path.extension().map(OsStr::to_ascii_lowercase),
                        path.file_name().and_then(OsStr::to_str),
                    ) {
                        return self.drag_and_drop(&path, &extension, filename);
                    }
                }
                iced::window::Event::Closed
                | iced::window::Event::RedrawRequested(_)
                | iced::window::Event::Moved { .. }
                | iced::window::Event::Opened { .. }
                | iced::window::Event::Focused
                | iced::window::Event::Unfocused => {}
            },
            iced::Event::Keyboard(event) => match event {
                keyboard::Event::KeyPressed {
                    key,
                    // location,
                    modifiers,
                    ..
                } => {
                    return self.handle_key_press(key, modifiers, status);
                }
                keyboard::Event::KeyReleased { key, .. } => {
                    self.keys_pressed.remove(&key);
                }
                keyboard::Event::ModifiersChanged(modifiers) => {
                    if let iced::event::Status::Ignored = status {
                        self.modifiers_pressed = modifiers;
                    }
                }
            },
            iced::Event::Mouse(mouse) => match mouse {
                iced::mouse::Event::CursorMoved { position } => {
                    let pos = (position.x, position.y);
                    self.window_state.mouse_pos = pos;
                }
                iced::mouse::Event::ButtonPressed(_) => {
                    if let iced::event::Status::Ignored = status {
                        self.hide_submenu();
                    }
                }
                _ => {}
            },
            iced::Event::Touch(_) => {}
        }
        Task::none()
    }

    fn handle_key_press(
        &mut self,
        key: Key,
        modifiers: keyboard::Modifiers,
        status: iced::event::Status,
    ) -> Task<Message> {
        let ignored = matches!(status, iced::event::Status::Ignored);
        if let (Key::Named(Named::Escape), true) = (key.clone(), ignored) {
            return self.key_escape_back(true).1;
        }

        if let Key::Character(ch) = &key {
            let msg = match (
                ch.as_str(),
                modifiers.command(),
                modifiers.alt(),
                ignored,
                &self.state,
            ) {
                ("q", true, _, true, _) => Message::CoreTryQuit,

                // ========
                // MANAGE MODS MENU
                // ========
                ("a", true, _, true, State::EditMods(_)) => ManageModsMessage::SelectAll.into(),
                // Ctrl-F search in mods list (with toggling)
                #[rustfmt::skip]
                ("f", true, _, _, State::EditMods(MenuEditMods { search: Some(_), .. })) => {
                    ManageModsMessage::SetSearch(None).into()
                },
                ("f", true, _, _, State::EditMods(_)) => Message::Multiple(vec![
                    ManageModsMessage::SetSearch(Some(String::new())).into(),
                    Message::CoreFocusNext,
                ]),

                // Search Action (general)
                #[rustfmt::skip]
                ("f", true, _, _,
                    State::Create(MenuCreateInstance::Choosing { .. }) | State::ModsDownload(_))
                | ("/", _, _, true,
                    State::Create(MenuCreateInstance::Choosing { .. }) | State::ModsDownload(_))
                => Message::CoreFocusNext,

                // Misc
                ("a", true, _, true, State::EditJarMods(_)) => {
                    crate::state::ManageJarModsMessage::SelectAll.into()
                }

                // ========
                // MAIN MENU
                // ========
                ("n", true, _, _, State::Launch(_)) => {
                    CreateInstanceMessage::ScreenOpen(ql_core::InstanceKind::Client).into()
                }
                ("1", ctrl, alt, _, State::Launch(_)) if ctrl | alt => {
                    MainMenuMessage::ChangeTab(LaunchTab::Buttons).into()
                }
                ("2", ctrl, alt, _, State::Launch(_)) if ctrl | alt => {
                    MainMenuMessage::ChangeTab(LaunchTab::Edit).into()
                }
                ("3", ctrl, alt, _, State::Launch(_)) if ctrl | alt => {
                    MainMenuMessage::ChangeTab(LaunchTab::Log).into()
                }
                (",", true, _, _, State::Launch(_)) => {
                    LauncherSettingsMessage::Open(LauncherSettingsTab::default()).into()
                }

                _ => Message::Nothing,
            };
            return Task::done(msg);
        } else if let State::LauncherSettings(menu) = &mut self.state {
            if let Key::Named(Named::ArrowUp) = key {
                return Task::done(LauncherSettingsMessage::Open(menu.selected_tab.prev()).into());
            } else if let Key::Named(Named::ArrowDown) = key {
                return Task::done(LauncherSettingsMessage::Open(menu.selected_tab.next()).into());
            }
        } else if let State::License(menu) = &mut self.state {
            if let Key::Named(Named::ArrowUp) = key {
                return Task::done(Message::LicenseChangeTab(menu.selected_tab.prev()));
            } else if let Key::Named(Named::ArrowDown) = key {
                return Task::done(Message::LicenseChangeTab(menu.selected_tab.next()));
            }
        } else if let (State::Launch(_), true) = (&self.state, ignored) {
            if let Key::Named(Named::ArrowUp) = key {
                return self.select_instance_recursive(InstSelectOperation::Up);
            } else if let Key::Named(Named::ArrowDown) = key {
                return self.select_instance_recursive(InstSelectOperation::Down);
            } else if let Key::Named(Named::Enter) = key {
                if modifiers.command() {
                    return Task::done(LaunchMessage::Start.into());
                }
            } else if let Key::Named(Named::Backspace) = key {
                if modifiers.command() {
                    return Task::done(LaunchMessage::Kill.into());
                }
            }
        } else if let State::Create(MenuCreateInstance::Choosing(MenuCreateInstanceChoosing {
            list: Ok(Some(_)),
            ..
        })) = &self.state
        {
            if let Key::Named(Named::Enter) = key {
                if modifiers.command() {
                    return Task::done(CreateInstanceMessage::Start.into());
                }
            }
        } else if let State::Welcome(menu) = &mut self.state {
            if let Key::Named(Named::Enter) = key {
                *menu = match menu {
                    MenuWelcome::P1InitialScreen => MenuWelcome::P2Theme,
                    MenuWelcome::P2Theme => MenuWelcome::P3Auth,
                    MenuWelcome::P3Auth => {
                        return Task::done(Message::MScreenOpen {
                            message: Some(InfoMessage::success(
                                "Install Minecraft by clicking \"+ New\"",
                            )),
                            clear_selection: true,
                        });
                    }
                };
            }
        }
        self.keys_pressed.insert(key);

        Task::none()
    }

    fn drag_and_drop(&mut self, path: &Path, extension: &OsStr, filename: &str) -> Task<Message> {
        if let State::EditMods(_) = &self.state {
            if extension == "jar" || extension == "disabled" {
                self.load_jar_from_path(path, filename);
                Task::none()
            } else if extension == "qmp" {
                self.load_qmp_from_path(path)
            } else if extension == "zip" || extension == "mrpack" {
                self.load_modpack_from_path(path.to_owned())
            } else {
                Task::none()
            }
        } else if let State::ManagePresets(_) = &self.state {
            if extension == "qmp" {
                self.load_qmp_from_path(path)
            } else if extension == "zip" || extension == "mrpack" {
                self.load_modpack_from_path(path.to_owned())
            } else {
                Task::none()
            }
        } else if let State::EditJarMods(menu) = &mut self.state {
            if extension == "jar" || extension == "zip" {
                Self::load_jarmods_from_path(
                    self.selected_instance.as_ref().unwrap(),
                    path,
                    filename,
                    &mut menu.jarmods,
                );
            }
            Task::none()
        } else if let State::InstallOptifine(MenuInstallOptifine::Choosing { .. }) = &mut self.state
        {
            if extension == "jar" || extension == "zip" {
                self.install_optifine_confirm(path)
            } else {
                Task::none()
            }
        } else {
            Task::none()
        }
    }

    fn load_jarmods_from_path(
        selected_instance: &Instance,
        path: &Path,
        filename: &str,
        jarmods: &mut JarMods,
    ) {
        let new_path = selected_instance
            .get_instance_path()
            .join("jarmods")
            .join(filename);
        if path != new_path {
            if let Err(err) = std::fs::copy(path, &new_path) {
                err!("Couldn't drag and drop mod file in: {err}");
            } else if !jarmods.mods.iter().any(|n| n.filename == filename) {
                jarmods.mods.push(JarMod {
                    filename: filename.to_owned(),
                    enabled: true,
                });
            }
        }
    }

    pub fn key_escape_back(&mut self, affect: bool) -> (bool, Task<Message>) {
        let mut ret_to_main_screen = false;
        let mut ret_to_mods = false;
        let mut ret_to_mod_store = false;

        if affect && self.hide_submenu() {
            return (true, Task::none());
        }

        match &self.state {
            State::ChangeLog
            | State::EditMods(MenuEditMods {
                mod_update_progress: None,
                ..
            })
            | State::Create(MenuCreateInstance::Choosing { .. })
            | State::Error { .. }
            | State::LauncherSettings(_)
            | State::LoginMS(MenuLoginMS { .. })
            | State::AccountLogin
            | State::ExportInstance(MenuExportInstance { progress: None, .. })
            | State::LoginAlternate(MenuLoginAlternate {
                is_loading: false, ..
            })
            | State::CreateShortcut(_)
            | State::Welcome(_) => {
                ret_to_main_screen = true;
            }
            #[cfg(feature = "auto_update")]
            State::UpdateFound(crate::state::MenuLauncherUpdate { progress: None, .. }) => {
                ret_to_main_screen = true;
            }

            State::License(_) => {
                if affect {
                    self.go_to_launcher_settings(LauncherSettingsTab::About);
                }
                return (true, Task::none());
            }
            State::ConfirmAction { no, .. } => {
                if affect {
                    return (true, self.update(no.clone()));
                }
            }
            State::InstallOptifine(MenuInstallOptifine::Choosing { .. })
            | State::InstallFabric(
                MenuInstallFabric::Loading { .. }
                | MenuInstallFabric::Loaded { progress: None, .. },
            )
            | State::EditJarMods(_)
            | State::ExportMods(_)
            | State::ManagePresets(MenuEditPresets {
                is_building: false,
                progress: None,
                ..
            })
            | State::RecommendedMods(
                MenuRecommendedMods::Loaded { .. }
                | MenuRecommendedMods::InstallALoader
                | MenuRecommendedMods::NotSupported,
            )
            | State::InstallPaper(
                MenuInstallPaper::Loading { .. } | MenuInstallPaper::Loaded { .. },
            )
            | State::ModDescription(_) => {
                ret_to_mods = true;
            }
            State::ModsDownload(menu) if menu.opened_mod.is_some() => {
                ret_to_mod_store = true;
            }
            State::ModsDownload(menu) if menu.mods_download_in_progress.is_empty() => {
                ret_to_mods = true;
            }
            #[cfg(feature = "auto_update")]
            State::UpdateFound(_) => {}
            State::InstallPaper(_)
            | State::ExportInstance(_)
            | State::InstallForge(_)
            | State::InstallJava
            | State::InstallOptifine(_)
            | State::InstallFabric(_)
            | State::EditMods(_)
            | State::Create(_)
            | State::ManagePresets(_)
            | State::ModsDownload(_)
            | State::GenericMessage(_)
            | State::AccountLoginProgress(_)
            | State::ImportModpack(_)
            | State::CurseforgeManualDownload(_)
            | State::LoginAlternate(_)
            | State::LogUploadResult { .. }
            | State::RecommendedMods(MenuRecommendedMods::Loading { .. })
            | State::Launch(_) => {}
        }

        if affect {
            if ret_to_main_screen {
                return (true, self.go_to_main_menu(None));
            }
            if ret_to_mods {
                return (true, self.go_to_edit_mods_menu(None));
            }
            if ret_to_mod_store {
                if let State::ModsDownload(menu) = &mut self.state {
                    menu.opened_mod = None;
                    menu.description = None;
                    return (
                        true,
                        iced::widget::scrollable::scroll_to(
                            iced::widget::scrollable::Id::new("MenuModsDownload:main:mods_list"),
                            menu.scroll_offset,
                        ),
                    );
                }
            }
        }

        (
            ret_to_main_screen | ret_to_mods | ret_to_mod_store,
            Task::none(),
        )
    }

    pub fn hide_submenu(&mut self) -> bool {
        if let State::EditMods(menu) = &mut self.state {
            if menu.modal.is_some() {
                menu.modal = None;
                return true;
            }
            if menu.search.is_some() {
                menu.search = None;
                return true;
            }
        } else if let State::Create(MenuCreateInstance::Choosing(MenuCreateInstanceChoosing {
            show_category_dropdown,
            ..
        })) = &mut self.state
        {
            if *show_category_dropdown {
                *show_category_dropdown = false;
                return true;
            }
        } else if let State::Launch(menu) = &mut self.state {
            if menu.modal.is_some() {
                menu.modal = None;
                return true;
            }
        }
        false
    }
}
