/**
 * Modelo de layout del escritorio tipo launcher (orden lógico + carpetas).
 * Desacoplado del DOM; usa IDs estables (title + link/url) alineados con getAppStableId.
 */
(function (global) {
    'use strict';

    var LAUNCHER_LAYOUT_VERSION = 1;
    var DEFAULT_FOLDER_NAME = 'Carpeta';
    var DEFAULT_FOLDER_COLOR = '#5c6bc0';

    function isValidHexColor(c) {
        return typeof c === 'string' && /^#[0-9A-Fa-f]{6}$/.test(c);
    }

    /**
     * Normaliza objeto launcherLayout leído de settings.
     */
    function normalizeLayout(raw) {
        if (!raw || typeof raw !== 'object') {
            return { version: LAUNCHER_LAYOUT_VERSION, order: [], folders: {} };
        }
        var version = typeof raw.version === 'number' ? raw.version : 1;
        var order = [];
        if (Array.isArray(raw.order)) {
            raw.order.forEach(function (entry) {
                var n = normalizeOrderEntry(entry);
                if (n) order.push(n);
            });
        }
        var folders = {};
        if (raw.folders && typeof raw.folders === 'object') {
            Object.keys(raw.folders).forEach(function (fid) {
                var f = raw.folders[fid];
                if (!f || typeof f !== 'object') return;
                var ids = [];
                if (Array.isArray(f.appStableIds)) ids = f.appStableIds.map(String);
                else if (Array.isArray(f.appIds)) ids = f.appIds.map(String);
                folders[fid] = {
                    name: typeof f.name === 'string' && f.name.trim() ? f.name.trim() : DEFAULT_FOLDER_NAME,
                    color: isValidHexColor(f.color) ? f.color : DEFAULT_FOLDER_COLOR,
                    appStableIds: ids,
                    updatedAt: typeof f.updatedAt === 'number' ? f.updatedAt : Date.now()
                };
            });
        }
        return { version: version, order: order, folders: folders };
    }

    function normalizeOrderEntry(entry) {
        if (!entry || typeof entry !== 'object') return null;
        if (entry.kind === 'app' && entry.appStableId) {
            return { kind: 'app', appStableId: String(entry.appStableId) };
        }
        if (entry.kind === 'folder' && entry.folderId) {
            return { kind: 'folder', folderId: String(entry.folderId) };
        }
        return null;
    }

    /**
     * Migra desde el orden implícito del array apps (solo apps no sistema).
     */
    function migrateFromApps(apps, getStableId) {
        var layout = normalizeLayout(null);
        if (!Array.isArray(apps)) return layout;
        apps.forEach(function (app) {
            if (!app || app.isSystemApp) return;
            var sid = getStableId(app);
            if (sid) layout.order.push({ kind: 'app', appStableId: sid });
        });
        return layout;
    }

    /**
     * Asegura layout en settings: si falta o está vacío, deriva de apps.
     */
    function ensure(settings, getStableId) {
        var raw = settings.launcherLayout;
        var layout = normalizeLayout(raw);
        if (!raw || !Array.isArray(layout.order) || layout.order.length === 0) {
            layout = migrateFromApps(settings.apps || [], getStableId);
        }
        layout.version = LAUNCHER_LAYOUT_VERSION;
        return layout;
    }

    function getFolderMembershipMap(layout) {
        var map = new Map();
        var folders = layout.folders || {};
        Object.keys(folders).forEach(function (fid) {
            var ids = folders[fid].appStableIds || [];
            ids.forEach(function (sid) {
                map.set(sid, fid);
            });
        });
        return map;
    }

    function findAppByStableId(apps, sid, getStableId) {
        for (var i = 0; i < apps.length; i++) {
            var a = apps[i];
            if (!a) continue;
            if (getStableId(a) === sid) return { app: a, index: i };
        }
        return null;
    }

    /**
     * Elimina referencias rotas (apps que ya no existen en catálogo).
     */
    function pruneOrphans(layout, apps, getStableId) {
        var catalog = new Set();
        apps.forEach(function (a) {
            if (a && !a.isSystemApp) catalog.add(getStableId(a));
        });
        var folderMap = getFolderMembershipMap(layout);
        var folders = Object.assign({}, layout.folders);
        Object.keys(folders).forEach(function (fid) {
            var f = folders[fid];
            f.appStableIds = (f.appStableIds || []).filter(function (sid) {
                return catalog.has(sid);
            });
            f.updatedAt = Date.now();
        });
        var newOrder = [];
        (layout.order || []).forEach(function (slot) {
            if (slot.kind === 'app') {
                if (catalog.has(slot.appStableId)) newOrder.push(slot);
            } else if (slot.kind === 'folder') {
                var fo = folders[slot.folderId];
                if (fo && (fo.appStableIds || []).length > 0) newOrder.push(slot);
                else if (fo && (fo.appStableIds || []).length === 0) delete folders[slot.folderId];
            }
        });
        return { version: layout.version, order: newOrder, folders: folders };
    }

    function appendAppToOrder(layout, appStableId, getStableId) {
        var layout2 = normalizeLayout(layout);
        var inFolder = getFolderMembershipMap(layout2);
        if (inFolder.has(appStableId)) return layout2;
        var existsTop = layout2.order.some(function (s) {
            return s.kind === 'app' && s.appStableId === appStableId;
        });
        if (!existsTop) {
            layout2.order.push({ kind: 'app', appStableId: appStableId });
        }
        return layout2;
    }

    /**
     * Cuando cambia el título (u otro dato que altere getStableId), reasigna el id en orden y carpetas.
     */
    function remapAppStableId(layout, oldSid, newSid) {
        if (!oldSid || !newSid || oldSid === newSid) return normalizeLayout(layout);
        var layout2 = normalizeLayout(layout);
        layout2.order = (layout2.order || []).map(function (slot) {
            if (slot && slot.kind === 'app' && slot.appStableId === oldSid) {
                return { kind: 'app', appStableId: newSid };
            }
            return slot;
        });
        var folders = Object.assign({}, layout2.folders);
        Object.keys(folders).forEach(function (fid) {
            var f = Object.assign({}, folders[fid]);
            var ids = (f.appStableIds || []).map(function (id) {
                return id === oldSid ? newSid : id;
            });
            var seen = {};
            f.appStableIds = ids.filter(function (id) {
                if (seen[id]) return false;
                seen[id] = true;
                return true;
            });
            f.updatedAt = Date.now();
            folders[fid] = f;
        });
        layout2.folders = folders;
        return layout2;
    }

    function removeAppEverywhere(layout, appStableId) {
        var layout2 = normalizeLayout(layout);
        var folders = Object.assign({}, layout2.folders);
        Object.keys(folders).forEach(function (fid) {
            var f = Object.assign({}, folders[fid]);
            f.appStableIds = (f.appStableIds || []).filter(function (id) {
                return id !== appStableId;
            });
            f.updatedAt = Date.now();
            folders[fid] = f;
        });
        layout2.order = layout2.order.filter(function (slot) {
            if (slot.kind === 'app') return slot.appStableId !== appStableId;
            var fo = folders[slot.folderId];
            return fo && (fo.appStableIds || []).length > 0;
        });
        Object.keys(folders).forEach(function (fid) {
            if ((folders[fid].appStableIds || []).length === 0) delete folders[fid];
        });
        layout2.order = layout2.order.filter(function (slot) {
            if (slot.kind === 'folder') return !!folders[slot.folderId];
            return true;
        });
        layout2.folders = folders;
        return layout2;
    }

    function moveOrderIndex(layout, fromIndex, toIndex) {
        var layout2 = normalizeLayout(layout);
        var o = layout2.order.slice();
        if (fromIndex < 0 || fromIndex >= o.length) return layout2;
        if (toIndex < 0) toIndex = 0;
        if (toIndex >= o.length) toIndex = o.length - 1;
        var item = o.splice(fromIndex, 1)[0];
        o.splice(toIndex, 0, item);
        layout2.order = o;
        return layout2;
    }

    /**
     * Resuelve qué mostrar en el grid según filtros del escritorio.
     * opts: { apps, launcherLayout, getStableId, appsToShow (array filtrado actual), isFiltering, desktopModeGrid, isMobileDevice, mobileDesktopMode }
     */
    function resolveGridRows(opts) {
        var apps = opts.apps || [];
        var layout = normalizeLayout(opts.launcherLayout);
        var getStableId = opts.getStableId;
        var appsToShow = opts.appsToShow || [];
        var isFiltering = !!opts.isFiltering;

        var visibleSidSet = new Set();
        appsToShow.forEach(function (a) {
            if (!a) return;
            visibleSidSet.add(getStableId(a));
        });

        var membership = getFolderMembershipMap(layout);
        var rows = [];

        function appPassesFilter(app) {
            if (!app) return false;
            return visibleSidSet.has(getStableId(app));
        }

        function folderMatchesSearch(folder, folderId) {
            if (!isFiltering) return true;
            var q = (opts.searchQuery || '').toLowerCase().trim();
            if (!q) return true;
            if ((folder.name || '').toLowerCase().indexOf(q) !== -1) return true;
            var ids = folder.appStableIds || [];
            for (var i = 0; i < ids.length; i++) {
                var found = findAppByStableId(apps, ids[i], getStableId);
                if (found && found.app) {
                    var t = (found.app.title || '').toLowerCase();
                    var d = (found.app.description || '').toLowerCase();
                    if (t.indexOf(q) !== -1 || d.indexOf(q) !== -1) return true;
                }
            }
            return false;
        }

        (layout.order || []).forEach(function (slot, orderIndex) {
            if (slot.kind === 'app') {
                var sid = slot.appStableId;
                if (membership.has(sid)) return;
                var found = findAppByStableId(apps, sid, getStableId);
                if (!found || !found.app) return;
                if (!appPassesFilter(found.app)) return;
                rows.push({
                    kind: 'app',
                    app: found.app,
                    indexInApps: found.index,
                    orderIndex: orderIndex
                });
            } else if (slot.kind === 'folder') {
                var fid = slot.folderId;
                var folder = layout.folders[fid];
                if (!folder) return;
                if (isFiltering && !folderMatchesSearch(folder, fid)) return;
                rows.push({
                    kind: 'folder',
                    folderId: fid,
                    folder: folder,
                    orderIndex: orderIndex
                });
            }
        });

        return rows;
    }

    global.GridLauncher = global.GridLauncher || {};
    global.GridLauncher.layoutModel = {
        LAUNCHER_LAYOUT_VERSION: LAUNCHER_LAYOUT_VERSION,
        DEFAULT_FOLDER_NAME: DEFAULT_FOLDER_NAME,
        DEFAULT_FOLDER_COLOR: DEFAULT_FOLDER_COLOR,
        normalizeLayout: normalizeLayout,
        migrateFromApps: migrateFromApps,
        ensure: ensure,
        getFolderMembershipMap: getFolderMembershipMap,
        findAppByStableId: findAppByStableId,
        pruneOrphans: pruneOrphans,
        appendAppToOrder: appendAppToOrder,
        remapAppStableId: remapAppStableId,
        removeAppEverywhere: removeAppEverywhere,
        moveOrderIndex: moveOrderIndex,
        resolveGridRows: resolveGridRows,
        isValidHexColor: isValidHexColor
    };
})(typeof window !== 'undefined' ? window : this);
