// Parse default app categories from JSON
public static List<QuickSetup.CategoryData> get_default_app_categories() {
    return parse_app_categories_from_json_string(QuickSetup.DEFAULT_APP_CATEGORIES_JSON);
}

// Parse app categories from JSON string
public static List<QuickSetup.CategoryData> parse_app_categories_from_json_string(string json_string) {
    try {
        var parser = new Json.Parser();
        parser.load_from_data(json_string);
        var root_obj = parser.get_root().get_object();

        if (!root_obj.has_member("apps")) {
            warning("App categories JSON missing 'apps' member");
            return new List<QuickSetup.CategoryData>();
        }

        var apps_obj = root_obj.get_object_member("apps");
        if (!apps_obj.has_member("categories")) {
            warning("App categories JSON missing 'categories' member");
            return new List<QuickSetup.CategoryData>();
        }

        var categories_array = apps_obj.get_array_member("categories");
        var categories = new List<QuickSetup.CategoryData>();
        var length = categories_array.get_length();

        for (int i = 0; i < length; i++) {
            var category_obj = categories_array.get_object_element(i);
            var category = new QuickSetup.CategoryData();

            category.name = QuickSetup.tr(category_obj.get_string_member("name"));
            category.description = QuickSetup.tr(category_obj.get_string_member("description"));
            category.icon_name = category_obj.get_string_member("icon_name");
            category.sub_categories = new List<QuickSetup.SubCategoryData>();
            category.apps = new List<QuickSetup.AppData>();
            category.selected_apps = new List<QuickSetup.AppData>();

            // Parse subcategories
            if (category_obj.has_member("subcategories")) {
                var subcategories_array = category_obj.get_array_member("subcategories");
                var sub_length = subcategories_array.get_length();

                for (int j = 0; j < sub_length; j++) {
                    var sub_obj = subcategories_array.get_object_element(j);
                    var sub_category = new QuickSetup.SubCategoryData();
                    sub_category.id = sub_obj.get_string_member("id");
                    sub_category.name = QuickSetup.tr(sub_obj.get_string_member("name"));
                    sub_category.description = QuickSetup.tr(sub_obj.get_string_member("description"));
                    category.sub_categories.append(sub_category);
                }
            }

            // Parse apps
            if (category_obj.has_member("apps")) {
                var apps_array = category_obj.get_array_member("apps");
                var apps_length = apps_array.get_length();

                for (int j = 0; j < apps_length; j++) {
                    var app_obj = apps_array.get_object_element(j);
                    var app = new QuickSetup.AppData();
                    app.name = QuickSetup.tr(app_obj.get_string_member("name"));
                    app.app_id = app_obj.get_string_member("app_id");
                    app.description = QuickSetup.tr(app_obj.get_string_member("description"));
                    app.styleclass = normalize_app_catalog_styleclass(
                        app.description,
                        app_obj.has_member("styleclass") ? app_obj.get_string_member("styleclass") : ""
                    );
                    app.sub_category = app_obj.has_member("sub_category") ? app_obj.get_string_member("sub_category") : null;
                    app.package_manager = app_obj.has_member("package_manager") ? app_obj.get_string_member("package_manager") : "flathub";
                    app.checked = false;
                    category.apps.append(app);
                }
            }

            categories.append(category);
        }

        return categories;
    } catch (Error e) {
        warning("Failed to parse app categories JSON: %s", e.message);
        return new List<QuickSetup.CategoryData>();
    }
}

private static string normalize_app_catalog_styleclass(string description, string styleclass) {
    if (styleclass == "success") {
        var normalized_description = description.down();
        bool is_preinstalled = normalized_description.contains("preinstalled") || normalized_description.contains("pre-installed");
        if (!is_preinstalled) {
            return "";
        }
    }

    return styleclass;
}

// Load app categories from config or use defaults
public static List<QuickSetup.CategoryData> load_app_categories_from_config(QuickSetup.PageContext context) {
    var object = context.config.get_page_object("apps");

    // If no config exists, use default JSON
    if (object == null || !object.has_member("categories")) {
        return parse_app_categories_from_json_string(QuickSetup.DEFAULT_APP_CATEGORIES_JSON);
    }

    // Convert JsonObject back to JSON string and parse using the common function
    var generator = new Json.Generator();
    var node = new Json.Node(Json.NodeType.OBJECT);
    node.set_object(object);
    generator.set_root(node);
    string json_string = generator.to_data(null);

    return parse_app_categories_from_json_string(json_string);
}

namespace QuickSetup {
    public class AppsPage : QsPage {
        private Adw.PreferencesPage selection_page;
        private Adw.PreferencesGroup recommended_group;
        private Adw.PreferencesGroup categories_group;
        private Adw.ActionRow recommended_row;
        private List<CategoryData> categories;
        private CategoryData? recommended_category;
        private CategoryData? utilities_category;
        private HashTable<string, AppData> shared_apps;

        public override string page_id { get { return "apps"; } }
        public override bool can_go_back_to { get { return true; } }
        public override bool needs_network { get { return true; } }

        public AppsPage(PageContext context) {
            base(context, "Essential Apps");

            this.categories = load_app_categories_from_config(context);
            this.add_browser_category();
            this.shared_apps = new HashTable<string, AppData>(str_hash, str_equal);
            this.register_existing_apps();
            this.setup_ui();
            this.load_curated_catalog();
        }

        private void setup_ui() {
            this.selection_page = new Adw.PreferencesPage();
            this.selection_page.title = tr("Application Categories");
            //  this.selection_page.description = "Select a category to browse and install applications";
            this.selection_page.icon_name = "applications-other-symbolic";
            this.selection_page.vexpand = true;
            this.selection_page.hexpand = true;
            this.selection_page.valign = Gtk.Align.CENTER;
            this.selection_page.halign = Gtk.Align.FILL;

            this.recommended_group = new Adw.PreferencesGroup();
            this.recommended_group.title = tr("Recommended");
            this.recommended_group.description = tr("Curated app picks for a fresh stillOS install");

            this.recommended_row = new Adw.ActionRow();
            this.recommended_row.title = tr("Default Apps");
            this.recommended_row.subtitle = tr("Curated selection of apps that will be preinstalled.");
            this.recommended_row.add_prefix(new Gtk.Image.from_icon_name("starred-symbolic"));
            this.recommended_row.add_suffix(new Gtk.Image.from_icon_name("go-next-symbolic"));
            this.recommended_row.set_activatable(true);
            this.recommended_row.activated.connect(() => {
                if (this.recommended_category != null) {
                    this.open_category_page(this.recommended_category, "recommended-apps");
                }
            });
            this.recommended_group.add(this.recommended_row);
            this.selection_page.add(this.recommended_group);

            this.categories_group = new Adw.PreferencesGroup();
            this.categories_group.title = tr("Categories");
            this.categories_group.description = tr("Choose a category to install essential applications");

            foreach (var category in this.categories) {
                this.add_category_row(this.categories_group, category, this.get_page_id_for_category(category));
            }

            this.selection_page.add(this.categories_group);

            // Add Next button bar
            var next_group = new Adw.PreferencesGroup();
            var button_bar = this.create_trailing_button_bar();
            var next_button = this.create_page_button("Next", true);
            next_button.clicked.connect(() => {
                this.next_button_clicked();
            });
            button_bar.append(next_button);
            next_group.add(button_bar);
            this.selection_page.add(next_group);

            this.add_content(this.selection_page);
        }

        private void register_existing_apps() {
            foreach (var category in this.categories) {
                foreach (var app in category.apps) {
                    if (app.app_id != null && app.app_id.strip() != "") {
                        this.shared_apps.set(app.app_id, app);
                    }
                }
            }
        }

        private void load_curated_catalog() {
            this.recommended_category = this.create_category(
                tr("Default Apps"),
                "Popular stillOS app picks",
                "starred-symbolic"
            );

            this.add_curated_app("com.github.tchx84.Flatseal", "Flatseal", "Manage Flatpak permissions", true);
            this.add_curated_app("de.haeckerfelix.Shortwave", "Shortwave", "Listen to internet radio", true);
            this.add_curated_app("org.gnome.Contacts", "Contacts", "Manage your contacts", true);
            this.add_curated_app("ca.edestcroix.Recordbox", "Recordbox", "Browse and play your local music", true);
            this.add_curated_app("org.gnome.Podcasts", "Podcasts", "Listen to your favorite shows", true);
            this.add_curated_app("io.github.amit9838.mousam", "Mousam", "Check the forecast with a clean weather app", true);
            this.add_curated_app("org.gnome.SoundRecorder", "Sound Recorder", "A simple, modern sound recorder for GNOME", true);
            this.add_curated_app("com.github.maoschanz.drawing", "Drawing", "Edit screenshots or memes", true);
            this.add_curated_app("com.belmoussaoui.Authenticator", "Authenticator", "Generate two-factor codes", true);
            this.add_curated_app("org.gnome.World.Secrets", "Secrets", "Manage your passwords", true);
            this.add_curated_app("io.gitlab.news_flash.NewsFlash", "Newsflash", "Keep up with your feeds", true);
            this.add_curated_app("com.rafaelmardojai.Blanket", "Blanket", "Listen to ambient sounds", true);
            this.add_curated_app("org.onlyoffice.desktopeditors", "ONLYOFFICE Desktop Editors", "Office productivity suite", true);
            this.add_curated_app("app.drey.Warp", "Warp", "Fast and secure file transfer", true);
            this.add_curated_app("org.gnome.Showtime", "Video Player", "Watch without distraction", true);
            this.add_curated_app("org.gnome.Firmware", "Firmware", "Install firmware on devices", true);
            this.add_curated_app("it.mijorus.gearlever", "Gear Lever", "Manage AppImages", true);
            this.add_curated_app("org.gnome.NetworkDisplays", "GNOME Network Displays", "Screencasting for GNOME", true);
            this.add_curated_app("org.gnome.DejaDup", "Deja Dup Backups", "Protect yourself from data loss", true);

            this.add_curated_app("org.gnome.Music", "GNOME Music", "Play and organize your music collection", false);
            this.add_curated_app("org.gnome.Lollypop", "Lollypop", "Modern music player for local libraries", false);
            this.add_curated_app("org.blender.Blender", "Blender", "Free and open source 3D creation suite", false);
            this.add_curated_app("org.gnome.Weather", "GNOME Weather", "See current conditions and forecasts", false);
            this.add_curated_app("org.gnome.World.PikaBackup", "Pika Backup", "Back up files with simple restore tools", false);
            this.add_curated_app("org.vinegarhq.Sober", "Sober", "Roblox Launcher for Linux", false);
        }

        private void add_curated_app(string flatpak_id, string name, string summary, bool recommended) {
            var app = this.get_or_create_shared_app(flatpak_id, name, summary, recommended);
            if (recommended && this.recommended_category != null) {
                this.append_app_if_missing(this.recommended_category, app);
            }
            this.attach_app_to_category(app, flatpak_id, recommended);
        }

        private AppData get_or_create_shared_app(string flatpak_id, string name, string summary, bool recommended) {
            string app_ref = this.to_flatpak_ref(flatpak_id);
            string styleclass = this.get_curated_styleclass(recommended);
            var existing = this.shared_apps.get(app_ref);
            if (existing != null) {
                existing.name = name;
                existing.description = summary;
                existing.package_manager = "flathub";
                existing.styleclass = styleclass;
                if (recommended) {
                    existing.recommended = true;
                    existing.checked = true;
                }
                return existing;
            }

            // Replace the preinstalled ONLYOFFICE placeholder with the real Flathub app.
            if (flatpak_id == "org.onlyoffice.desktopeditors") {
                var placeholder = this.find_existing_app_by_name("ONLYOFFICE");
                if (placeholder != null) {
                    placeholder.name = name;
                    placeholder.app_id = app_ref;
                    placeholder.description = summary;
                    placeholder.package_manager = "flathub";
                    placeholder.styleclass = styleclass;
                    placeholder.recommended = recommended;
                    placeholder.checked = recommended;
                    this.shared_apps.set(app_ref, placeholder);
                    return placeholder;
                }
            }

            if (flatpak_id == "org.gnome.Music") {
                var existing_music = this.find_existing_app_by_name("GNOME Music");
                if (existing_music != null) {
                    existing_music.name = name;
                    existing_music.app_id = app_ref;
                    existing_music.description = summary;
                    existing_music.package_manager = "flathub";
                    existing_music.styleclass = styleclass;
                    existing_music.recommended = recommended;
                    existing_music.checked = recommended;
                    this.shared_apps.set(app_ref, existing_music);
                    return existing_music;
                }
            }

            var app = new AppData();
            app.name = name;
            app.app_id = app_ref;
            app.description = summary;
            app.package_manager = "flathub";
            app.styleclass = styleclass;
            app.recommended = recommended;
            app.checked = recommended;
            this.shared_apps.set(app_ref, app);
            return app;
        }

        private string get_curated_styleclass(bool recommended) {
            return recommended ? "success" : "";
        }

        private void add_browser_category() {
            var shared_browser_category = get_shared_web_browser_category(this.context);
            var browser_category = this.create_category(
                "Web Browsers",
                "Install another browser later if you want to switch",
                shared_browser_category.icon_name
            );
            browser_category.allow_success_styleclass = true;

            foreach (var sub_category in shared_browser_category.sub_categories) {
                browser_category.sub_categories.append(sub_category);
            }

            foreach (var app in shared_browser_category.apps) {
                browser_category.apps.append(app);
                if (app.checked) {
                    browser_category.selected_apps.append(app);
                }
            }

            this.categories.prepend(browser_category);
        }

        private AppData? find_existing_app_by_name(string name) {
            foreach (var category in this.categories) {
                foreach (var app in category.apps) {
                    if (app.name == name) {
                        return app;
                    }
                }
            }

            return null;
        }

        private void attach_app_to_category(AppData app, string flatpak_id, bool recommended) {
            string category_name;
            string subcategory_id;
            this.resolve_destination(flatpak_id, out category_name, out subcategory_id);

            var target_category = this.get_or_create_target_category(category_name);
            if (target_category == null) {
                return;
            }

            app.sub_category = subcategory_id;
            if (recommended) {
                this.move_app_to_top(target_category, app);
            } else {
                this.append_app_if_missing(target_category, app);
            }
        }

        private void resolve_destination(string flatpak_id, out string category_name, out string subcategory_id) {
            category_name = "Utilities";
            subcategory_id = "system_tools";

            switch (flatpak_id) {
                case "de.haeckerfelix.Shortwave":
                    category_name = "Music Listening";
                    subcategory_id = "music_streaming";
                    return;
                case "ca.edestcroix.Recordbox":
                    category_name = "Music Listening";
                    subcategory_id = "local_music";
                    return;
                case "org.gnome.Podcasts":
                    category_name = "Music Listening";
                    subcategory_id = "music_streaming";
                    return;
                case "com.rafaelmardojai.Blanket":
                    category_name = "Music Listening";
                    subcategory_id = "music_tools";
                    return;
                case "org.gnome.SoundRecorder":
                    category_name = "Creative Work";
                    subcategory_id = "audio_editing";
                    return;
                case "com.github.maoschanz.drawing":
                    category_name = "Creative Work";
                    subcategory_id = "graphics";
                    return;
                case "org.gnome.Showtime":
                    category_name = "Utilities";
                    subcategory_id = "media";
                    return;
                case "org.blender.Blender":
                    category_name = "Creative Work";
                    subcategory_id = "three_d";
                    return;
                case "org.onlyoffice.desktopeditors":
                    category_name = "Productivity";
                    subcategory_id = "office";
                    return;
                case "org.gnome.Contacts":
                    category_name = "Productivity";
                    subcategory_id = "communication";
                    return;
                case "io.gitlab.news_flash.NewsFlash":
                    category_name = "Utilities";
                    subcategory_id = "reading";
                    return;
                case "app.drey.Warp":
                case "org.gnome.NetworkDisplays":
                    category_name = "Utilities";
                    subcategory_id = "sharing";
                    return;
                case "org.gnome.Weather":
                case "io.github.amit9838.mousam":
                    category_name = "Utilities";
                    subcategory_id = "weather";
                    return;
                case "com.belmoussaoui.Authenticator":
                case "org.gnome.World.Secrets":
                    category_name = "Utilities";
                    subcategory_id = "security";
                    return;
                case "com.github.tchx84.Flatseal":
                case "org.gnome.Firmware":
                case "it.mijorus.gearlever":
                    category_name = "Utilities";
                    subcategory_id = "system_tools";
                    return;
                case "org.gnome.DejaDup":
                case "org.gnome.World.PikaBackup":
                    category_name = "Utilities";
                    subcategory_id = "backup";
                    return;
                case "org.gnome.Music":
                case "org.gnome.Lollypop":
                    category_name = "Music Listening";
                    subcategory_id = "local_music";
                    return;
                case "org.vinegarhq.Sober":
                    category_name = "Gaming";
                    subcategory_id = "gaming_tools";
                    return;
                default:
                    return;
            }
        }

        private CategoryData? get_or_create_target_category(string category_name) {
            foreach (var category in this.categories) {
                if (category.name == category_name) {
                    return category;
                }
            }

            if (category_name == "Utilities") {
                if (this.utilities_category == null) {
                    this.utilities_category = this.create_category(
                        "Utilities",
                        "System tools, weather, backup, media, security, and sharing utilities",
                        "applications-system-symbolic"
                    );

                    var security = new SubCategoryData();
                    security.id = "security";
                    security.name = tr("Security");
                    security.description = tr("Password managers and authentication tools");
                    this.utilities_category.sub_categories.append(security);

                    var system_tools = new SubCategoryData();
                    system_tools.id = "system_tools";
                    system_tools.name = tr("System Tools");
                    system_tools.description = tr("System configuration and utility apps");
                    this.utilities_category.sub_categories.append(system_tools);

                    var sharing = new SubCategoryData();
                    sharing.id = "sharing";
                    sharing.name = tr("Sharing");
                    sharing.description = tr("File transfer and sharing utilities");
                    this.utilities_category.sub_categories.append(sharing);

                    var backup = new SubCategoryData();
                    backup.id = "backup";
                    backup.name = tr("Backup");
                    backup.description = tr("Backup and restore tools");
                    this.utilities_category.sub_categories.append(backup);

                    var reading = new SubCategoryData();
                    reading.id = "reading";
                    reading.name = tr("Reading");
                    reading.description = tr("News and feed readers");
                    this.utilities_category.sub_categories.append(reading);

                    var weather = new SubCategoryData();
                    weather.id = "weather";
                    weather.name = tr("Weather");
                    weather.description = tr("Weather forecasts and conditions");
                    this.utilities_category.sub_categories.append(weather);

                    var media = new SubCategoryData();
                    media.id = "media";
                    media.name = tr("Media");
                    media.description = tr("Playback and viewing utilities");
                    this.utilities_category.sub_categories.append(media);

                    this.categories.append(this.utilities_category);
                    this.add_category_row(this.categories_group, this.utilities_category, "utilities");
                }

                return this.utilities_category;
            }

            return null;
        }

        private CategoryData create_category(string name, string description, string icon_name) {
            var category = new CategoryData();
            category.name = name;
            category.description = description;
            category.icon_name = icon_name;
            category.sub_categories = new List<SubCategoryData>();
            category.apps = new List<AppData>();
            category.selected_apps = new List<AppData>();
            return category;
        }

        private void append_app_if_missing(CategoryData category, AppData app) {
            foreach (var existing in category.apps) {
                if (existing == app || existing.app_id == app.app_id) {
                    return;
                }
            }

            category.apps.append(app);
        }

        private void move_app_to_top(CategoryData category, AppData app) {
            category.apps.remove(app);
            category.apps.prepend(app);
        }

        private string to_flatpak_ref(string flatpak_id) {
            return "app/" + flatpak_id + "/x86_64/stable";
        }

        private string get_page_id_for_category(CategoryData category) {
            return category.name.down().replace(" ", "-");
        }

        private void add_category_row(Adw.PreferencesGroup group, CategoryData category, string page_id) {
            var category_row = new Adw.ActionRow();
            category_row.title = category.name;
            category_row.subtitle = category.description;

            // Add category icon
            var icon = new Gtk.Image.from_icon_name(category.icon_name);
            icon.pixel_size = 24;
            category_row.add_prefix(icon);

            // Add arrow indicator
            var arrow = new Gtk.Image.from_icon_name("go-next-symbolic");
            category_row.add_suffix(arrow);

            // Make row clickable
            category_row.set_activatable(true);
            category_row.activated.connect(() => {
                this.open_category_page(category, page_id);
            });

            group.add(category_row);
        }

        private void open_category_page(CategoryData category, string page_id) {
            var category_page = new AppCategoryPage(this.context, category, page_id);
            this.context.navigation_view.push(category_page);
        }

        private void next_button_clicked() {
            pin_checked_browsers_in_categories(this.categories);
            if (this.recommended_category != null) {
                pin_checked_browsers(this.recommended_category.apps);
            }

            var queued_app_ids = new HashTable<string, bool>(str_hash, str_equal);

            foreach (var category in this.categories) {
                foreach (var app in category.apps) {
                    if (!app.checked || app.app_id == null || app.app_id.strip() == "") {
                        continue;
                    }

                    if (queued_app_ids.contains(app.app_id)) {
                        continue;
                    }

                    queued_app_ids.set(app.app_id, true);
                    install_app(app, "INSTALL");
                }
            }

            if (this.recommended_category != null) {
                foreach (var app in this.recommended_category.apps) {
                    if (!app.checked || app.app_id == null || app.app_id.strip() == "") {
                        continue;
                    }

                    if (queued_app_ids.contains(app.app_id)) {
                        continue;
                    }

                    queued_app_ids.set(app.app_id, true);
                    install_app(app, "INSTALL");
                }
            }

            // Browser installs and favorites are intentionally committed only
            // after the user completes this app-selection page.
            this.context.window.ensure_browser_pinned();

            // Move to next page
            this.context.window.push_next_page();
        }
    }

    public class AppCategoryPage : CategoryPage {
        public string real_page_id { get; set; }
        public override string page_id { get { return this.real_page_id; } }
        public override bool can_go_back_to { get { return true; } }

        public AppCategoryPage(PageContext context, CategoryData category, string page_id) {
            base(context, category);
            this.real_page_id = page_id;
            this.set_back_button(true);
        }
    }
}
