<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Flags Database</title>
    <link rel="stylesheet" href="css/styles.css">
</head>
<body>
    <h1>Управление Базой Данных Флагов</h1>
    
    <!-- Фильтры -->
    <div id="filters">
        <h2>Фильтры</h2>

        <!-- Кнопка для открытия/закрытия фильтров -->
        <button id="toggle-filters" class="toggle-filters-btn">Показать/Скрыть Фильтры</button>

        <!-- Контейнер фильтров -->
        <div id="filters-panel" class="filters-panel hidden">
            <div id="color-filters">
                <h3>Фильтр по Цветам</h3>
                <!-- Фильтры цветов будут здесь -->
            </div>
            <div id="type-filters">
                <h3>Фильтр по Типам</h3>
                <!-- Фильтры типов будут здесь -->
            </div>
            <div id="language-filters">
                <h3>Фильтр по Языкам</h3>
                <!-- Фильтры языков будут здесь -->
            </div>
            <!-- Добавьте другие фильтры при необходимости -->
        </div>
        
        <!-- Действия для Фильтров -->
        <div id="filter-actions">
            <button id="apply-filters">Применить Фильтры</button>
            <button id="clear-filters">Очистить Фильтры</button>
        </div>
    </div>
    
    <!-- Поиск и Выбор Типа Данных -->
    <div>        
        <input type="text" id="search-input" placeholder="Поиск...">
        <button id="search-button">Поиск</button>
        <button id="clear-search">Очистить Поиск</button>
        <button id="view-data">Просмотр</button>
    </div>
        
    <!-- Контейнер для Вывода Данных -->
    <div id="output"></div>
    
    <!-- Контейнер для Пагинации -->
    <div id="pagination"></div>
    
    <!-- Загружающий Спиннер -->
    <div id="loading-spinner">
        <p>Загрузка...</p>
        <!-- Вы можете улучшить это с помощью CSS-анимаций или использовать SVG спиннер -->
    </div>
    
    <!-- Подключение вашего JavaScript файла -->
    <script type="module" src="js/main.js"></script>
</body>
</html>

// main.js - Точка входа приложения, которая связывает все модули и инициализирует приложение.
import { initDB, loadData, selectedFilters, getAllRecords } from './db.js';
import { populateAllFilters, languagesMap } from './ui.js';
import { addFilterEventListeners, setupEventListeners, setupRealTimeFiltering, setupFilterToggle, setupInfiniteScroll } from './events.js';
import { showLoading, hideLoading } from './utils.js';

// Объект состояния пагинации
export const paginationState = {
    currentPage: 1,
    pageSize: 36,
    isLoading: false,
    hasMore: true,
    filteredFlags: []
};

/**
 * Инициализирует и загружает приложение
 */
export async function initializeApp() {
    try {
        const db = await initDB();
        await loadData(db);
        await populateAllFilters(db);
        addFilterEventListeners();
        setupEventListeners(); // Настраиваем обработчики для остальных кнопок
        setupRealTimeFiltering(db); // Настраиваем фильтрацию в реальном времени
        setupFilterToggle(); // Настраиваем переключение фильтров
        setupInfiniteScroll(db); // Настраиваем Infinite Scroll
        console.log("Приложение инициализировано успешно.");
        handleViewData(db); // Отображаем данные по умолчанию
    } catch (error) {
        console.error("Произошла ошибка при инициализации приложения. Пожалуйста, попробуйте позже.", error);
    }
}

/**
 * Показывает/обновляет данные в зависимости от выбранного типа данных и применённых фильтров
 * @param {IDBDatabase} db - Открытая база данных
 */
export async function handleViewData(db) {
    const searchQuery = document.getElementById("search-input").value.trim();

    // Сброс состояния пагинации
    paginationState.currentPage = 1;
    paginationState.hasMore = true;

    // Получаем все флаги, соответствующие фильтрам и поиску
    const allFlags = await getFilteredFlags(db, searchQuery, selectedFilters);
    paginationState.filteredFlags = allFlags;

    // Отображаем первую страницу флагов
    await viewFlagsAsCards(db, searchQuery, selectedFilters, paginationState.currentPage, paginationState.pageSize);
}

/**
 * Получает все флаги, соответствующие запросу поиска и фильтрам
 * @param {IDBDatabase} db - Открытая база данных
 * @param {string} searchQuery - Запрос поиска
 * @param {object} filters - Выбранные фильтры
 * @returns {Promise<Array>} - Массив флагов
 */
export async function getFilteredFlags(db, searchQuery, filters) {
    // Получаем все флаги
    const flags = await getAllRecords(db, "flags");
    const flagNames = await getAllRecords(db, "flagNames");

    // Создаём карты для быстрого доступа
    const flagNamesMap = flagNames.reduce((map, name) => {
        if (!map[name.flag_id]) {
            map[name.flag_id] = [];
        }
        map[name.flag_id].push(name);
        return map;
    }, {});

    // Дополняем карту оригинальными именами
    flags.forEach(flag => {
        if (flag.originalNames) {
            if (!flagNamesMap[flag.id]) {
                flagNamesMap[flag.id] = [];
            }
            flag.originalNames.forEach(originalName => {
                flagNamesMap[flag.id].push({
                    flag_id: flag.id,
                    language_id: originalName.language_id,
                    original_name: originalName.original_name,
                });
            });
        }
    });
    
    // Фильтруем флаги по запросу поиска и выбранным фильтрам
    let filteredFlags = flags.filter(flag => {
        // **Поиск**
        let matchesSearch = true;
        if (searchQuery) {
            const lowerSearch = searchQuery.toLowerCase();

            // Проверка по ID флага
            const flagMatches = flag.id.toLowerCase().includes(lowerSearch);

            // Проверка по названиям флага
            const flagNamesForFlag = flagNamesMap[flag.id] || [];
            const namesMatch = flagNamesForFlag.some(nameObj => {
                const shortName = (nameObj.short_name || "").toLowerCase();
                const longName = (nameObj.long_name || "").toLowerCase();
                const originalName = (nameObj.original_name || "").toLowerCase();
                return shortName.includes(lowerSearch) || longName.includes(lowerSearch) || originalName.includes(lowerSearch);
            });

            matchesSearch = flagMatches || namesMatch;
        }

        if (!matchesSearch) return false;

        // **Фильтрация по Цветам**
        if (filters.colors.size > 0) {
            const flagColors = new Set(flag.colors);
            const commonColors = [...filters.colors].some(color => flagColors.has(color));
            if (!commonColors) return false;
        }

        // **Фильтрация по Типам**
        if (filters.types.size > 0) {
            const flagTypes = new Set(flag.types);
            const commonTypes = [...filters.types].some(type => flagTypes.has(type));
            if (!commonTypes) return false;
        }

        // **Фильтрация по Языкам**
        if (filters.languages.size > 0) {
            const namesForFlag = flagNamesMap[flag.id] || [];
            const hasMatchingLanguage = namesForFlag.some(nameObj => filters.languages.has(nameObj.language_id));
            if (!hasMatchingLanguage) return false;
        }

        return true;
    });

    return filteredFlags;
}

/**
 * Показывает/обновляет данные в виде карточек с учётом фильтров и поиска с поддержкой пагинации
 * @param {IDBDatabase} db - Открытая база данных
 * @param {string} searchQuery - Запрос поиска
 * @param {object} filters - Выбранные фильтры
 * @param {number} page - Номер текущей страницы
 * @param {number} pageSize - Количество флагов на страницу
 * @returns {Promise<void>}
 */
export async function viewFlagsAsCards(db, searchQuery = "", filters = { colors: new Set(), types: new Set(), languages: new Set() }, page = 1, pageSize = 36) {
    try {
        if (page === 1) {
            showLoading(); // Показываем спиннер только при первой загрузке
        }

        // Получаем все необходимые данные
        const [flags, flagNames, colors, languages] = await Promise.all([
            getAllRecords(db, "flags"),
            getAllRecords(db, "flagNames"),
            getAllRecords(db, "colors"),
            getAllRecords(db, "languages")
        ]);

        // Создаём карты для быстрого доступа
        const flagNamesMap = flagNames.reduce((map, name) => {
            if (!map[name.flag_id]) {
                map[name.flag_id] = [];
            }
            map[name.flag_id].push(name);
            return map;
        }, {});

        // Дополняем карту оригинальными именами
        flags.forEach(flag => {
            if (flag.originalNames) {
                if (!flagNamesMap[flag.id]) {
                    flagNamesMap[flag.id] = [];
                }
                flag.originalNames.forEach(originalName => {
                    flagNamesMap[flag.id].push({
                        flag_id: flag.id,
                        language_id: originalName.language_id,
                        original_name: originalName.original_name,
                    });
                });
            }
        });

        const colorsMapLocal = colors.reduce((map, color) => {
            map[color.id] = color;
            return map;
        }, {});

        const languagesMapLocal = languages.reduce((map, language) => {
            map[language.id] = language.en_name || "Unknown Language";
            return map;
        }, {});

        // Фильтруем флаги по запросу поиска и выбранным фильтрам
        let filteredFlags = flags.filter(flag => {
            // **Поиск**
            let matchesSearch = true;
            if (searchQuery) {
                const lowerSearch = searchQuery.toLowerCase();

                // Проверка по ID флага
                const flagMatches = flag.id.toLowerCase().includes(lowerSearch);

                // Проверка по названиям флага
                const flagNamesForFlag = flagNamesMap[flag.id] || [];
                const namesMatch = flagNamesForFlag.some(nameObj => {
                    const shortName = (nameObj.short_name || "").toLowerCase();
                    const longName = (nameObj.long_name || "").toLowerCase();
                    const originalName = (nameObj.original_name || "").toLowerCase();
                    return shortName.includes(lowerSearch) || longName.includes(lowerSearch) || originalName.includes(lowerSearch);
                });

                matchesSearch = flagMatches || namesMatch;
            }

            if (!matchesSearch) return false;

            // **Фильтрация по Цветам**
            if (filters.colors.size > 0) {
                const flagColors = new Set(flag.colors);
                const commonColors = [...filters.colors].some(color => flagColors.has(color));
                if (!commonColors) return false;
            }

            // **Фильтрация по Типам**
            if (filters.types.size > 0) {
                const flagTypes = new Set(flag.types);
                const commonTypes = [...filters.types].some(type => flagTypes.has(type));
                if (!commonTypes) return false;
            }

            // **Фильтрация по Языкам**
            if (filters.languages.size > 0) {
                const namesForFlag = flagNamesMap[flag.id] || [];
                const hasMatchingLanguage = namesForFlag.some(nameObj => filters.languages.has(nameObj.language_id));
                if (!hasMatchingLanguage) return false;
            }

            return true;
        });

        // Определяем диапазон флагов для текущей страницы
        const startIndex = (page - 1) * pageSize;
        const endIndex = startIndex + pageSize;
        const flagsToDisplay = filteredFlags.slice(startIndex, endIndex);

        // Очищаем предыдущий вывод только если это первая страница
        const output = document.getElementById("output");
        if (page === 1) {
            output.innerHTML = "";
        }

        // Обрабатываем случай, когда на первой странице нет совпадений
        if (page === 1 && flagsToDisplay.length === 0) {
            output.innerHTML = "<p>Нет флагов, соответствующих выбранным фильтрам.</p>";
            hideLoading(); // Скрываем спиннер загрузки
            return;
        }

        // Создаём DocumentFragment для оптимизации DOM
        const fragment = document.createDocumentFragment();

        // Итерация по флагам для отображения и создание карточек
        for (const flag of flagsToDisplay) {
            // Создаём элемент карточки
            const card = document.createElement("div");
            card.className = "flag_card";

            // Индекс флага
            const flagIndex = document.createElement("span");
            flagIndex.className = "flag_index";
            flagIndex.textContent = flag.id;
            card.appendChild(flagIndex);

            // Изображение флага
            const flagImgDiv = document.createElement("div");
            flagImgDiv.className = "flag_img";
            const img = document.createElement("img");
            img.setAttribute("data-src", flag.parent_flag_id ? `flags/PNG-1200/PNG-0.5/${flag.parent_flag_id}-0.5-1200.png` : `flags/PNG-1200/PNG-0.5/${flag.id}-0.5-1200.png`);
            img.alt = flag.parent_flag_id ? `Флаг ${flag.parent_flag_id}` : `Флаг ${flag.id}`;
            img.classList.add("lazy-image");
            img.src = 'https://placehold.co/400'; // Заглушка до загрузки

            flagImgDiv.appendChild(img);
            card.appendChild(flagImgDiv);

            // Краткое название флага
            const shortName = document.createElement("div");
            shortName.className = "flag_shortname";

            const enNameObj = flagNamesMap[flag.id]?.find(fn => fn.language_id === "en");
            shortName.textContent = enNameObj ? enNameObj.short_name : "N/A";
            card.appendChild(shortName);

            // Полное название флага
            const longName = document.createElement("div");
            longName.className = "flag_longname";
            longName.textContent = flag.originalNames && flag.originalNames.length > 0
                ? flag.originalNames[0].original_name
                : "N/A";
            card.appendChild(longName);
            
            const namesForFlag = flagNamesMap[flag.id] || [];
            
            for (const nameObj of namesForFlag) {
                // Названия флага на разных языках
                const namesContainer = document.createElement("div");
                namesContainer.className = "flag_names_container";

                const langSpan = document.createElement("span");
                langSpan.className = "flag_original_lang";

                // Получаем название языка из предзагруженной карты языков
                const languageTitle = languagesMap[nameObj.language_id];
                langSpan.title = languageTitle || "Неизвестный язык"; // Всплывающая подсказка с названием языка
                langSpan.textContent = nameObj.language_id;
                namesContainer.appendChild(langSpan);

                const nameSpan = document.createElement("span");
                nameSpan.textContent = nameObj.short_name || nameObj.long_name || nameObj.original_name || "N/A";
                namesContainer.appendChild(nameSpan);

                card.appendChild(namesContainer);
            }

            // Цвета флага
            const colorsContainer = document.createElement("div");
            colorsContainer.className = "flag_colors_container";

            for (const colorId of flag.colors) {
                const colorObj = colorsMapLocal[colorId];
                if (colorObj) {
                    const colorDiv = document.createElement("div");
                    colorDiv.className = "flag_color";
                    colorDiv.title = colorObj.en_name; // Всплывающая подсказка с названием цвета
                    colorDiv.style.backgroundColor = colorObj.hex;
                    colorsContainer.appendChild(colorDiv);
                }
            }

            card.appendChild(colorsContainer);

            // Добавляем информацию о подразделениях
            const subdivisionsInfo = await getFlagSubdivisions(db, flag.id);
            if (subdivisionsInfo.length > 0) {
                const subdivisionsContainer = document.createElement("div");
                subdivisionsContainer.className = "flag_subdivisions_container hidden"; // Скрываем по умолчанию

                const subdivisionsTitle = document.createElement("h4");
                subdivisionsTitle.textContent = "Подразделения:";
                subdivisionsContainer.appendChild(subdivisionsTitle);

                subdivisionsInfo.forEach(sub => {
                    const subItem = document.createElement("p");
                    subItem.textContent = sub.en_name || sub.ru_name || sub.zh_name || "N/A"; // Используем доступные имена
                    subdivisionsContainer.appendChild(subItem);
                });

                // Добавляем кнопку для раскрытия/скрытия подразделений
                const toggleSubsBtn = document.createElement("button");
                toggleSubsBtn.textContent = "Показать Подразделения";
                toggleSubsBtn.className = "toggle-subs-btn";

                toggleSubsBtn.addEventListener("click", () => {
                    subdivisionsContainer.classList.toggle("hidden");
                    toggleSubsBtn.textContent = subdivisionsContainer.classList.contains("hidden") ? "Показать Подразделения" : "Скрыть Подразделения";
                });

                card.appendChild(toggleSubsBtn);
                card.appendChild(subdivisionsContainer);
            }

            // Добавляем карточку в фрагмент
            fragment.appendChild(card);
        }

        // Добавляем все карточки в контейнер вывода
        output.appendChild(fragment);
        hideLoading();

        // Инициализируем ленивую загрузку изображений
        initLazyLoadImages();
    }
    catch (error){
        console.log("Произошла ошибка при отображении флагов. Пожалуйста, попробуйте позже.", error);
        hideLoading(); // Убедиться, что спиннер скрыт в случае ошибки
    }
}
	

/**
 * Инициализирует ленивую загрузку изображений с использованием Intersection Observer
 */
function initLazyLoadImages() {
    const lazyImages = document.querySelectorAll("img.lazy-image");

    if ("IntersectionObserver" in window) {
        const imageObserver = new IntersectionObserver((entries, observer) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    const img = entry.target;
                    const realSrc = img.getAttribute("data-src");
                    if (realSrc) {
                        img.src = realSrc;
                        img.removeAttribute("data-src");
                    }
                    imageObserver.unobserve(img);
                }
            });
        }, {
            rootMargin: "50px 0px", // Предзагрузка за 50px до появления
            threshold: 0.01
        });

        lazyImages.forEach(img => {
            imageObserver.observe(img);
        });
    } else {
        // Fallback для браузеров без поддержки Intersection Observer
        lazyImages.forEach(img => {
            const realSrc = img.getAttribute("data-src");
            if (realSrc) {
                img.src = realSrc;
                img.removeAttribute("data-src");
            }
        });
    }
}

/**
 * Получает подразделения флага
 * @param {IDBDatabase} db - Открытая база данных
 * @param {string} flagId - ID флага
 * @returns {Promise<Array>} - Массив подразделений с названиями
 */
export async function getFlagSubdivisions(db, flagId) {
    try {
        // Получаем все подразделения для данного флага
        const subdivisions = await new Promise((resolve, reject) => {
            const transaction = db.transaction(["flagSubdivisions"], "readonly");
            const store = transaction.objectStore("flagSubdivisions");
            const index = store.index("parent_flag_id");
            const request = index.getAll(IDBKeyRange.only(flagId));

            request.onsuccess = function(event) {
                resolve(event.target.result);
            };

            request.onerror = function(event) {
                console.error(`Ошибка при получении подразделений для флага "${flagId}":`, event.target.error);
                resolve([]); // Возвращаем пустой массив в случае ошибки
            };
        });

        if (subdivisions.length === 0) {
            return [];
        }

        // Получаем названия подразделений
        const subdivisionIds = subdivisions.map(sub => sub.id);
        const subdivisionNames = await new Promise((resolve, reject) => {
            const transaction = db.transaction(["flagSubdivisionNames"], "readonly");
            const store = transaction.objectStore("flagSubdivisionNames");
            const request = store.getAll(); // Получаем все и фильтруем в коде

            request.onsuccess = function(event) {
                const allNames = event.target.result;
                const matchedNames = allNames.filter(name => subdivisionIds.includes(name.id));
                resolve(matchedNames);
            };

            request.onerror = function(event) {
                console.error(`Ошибка при получении названий подразделений для флага "${flagId}":`, event.target.error);
                resolve([]); // Возвращаем пустой массив в случае ошибки
            };
        });

        return subdivisionNames;
    } catch (error) {
        console.error("Ошибка при получении подразделений флага:", error);
        return [];
    }
}

document.addEventListener("DOMContentLoaded", initializeApp);

// ui.js - Управляет всеми функциями, связанными с изменением интерфейса пользователя, такими как отображение фильтров и карточек флагов.
import { getAllRecords } from './db.js';

// Объявление необходимых переменных и карт
export let colorsMap = {};
export let languagesMap = {};

/**
 * Заполняет фильтры на основе данных из базы
 * @param {IDBDatabase} db - Открытая база данных
 * @returns {Promise<void>}
 */
export async function populateAllFilters(db) {
    await populateColorFilters(db);
    await populateTypeFilters(db);
    await populateLanguageFilters(db);
}

/**
 * Заполняет фильтр по цветам
 * @param {IDBDatabase} db - Открытая база данных
 * @returns {Promise<void>}
 */
export async function populateColorFilters(db) {
    const colors = await getAllRecords(db, "colors");
    const colorFiltersDiv = document.getElementById("color-filters");

    colorsMap = colors.reduce((map, color) => {
        map[color.id] = color;
        return map;
    }, {});

    colors.forEach(color => {
        const label = document.createElement("label");
        label.classList.add("filter-option");
        label.style.display = "flex";
        label.style.alignItems = "center";

        const checkbox = document.createElement("input");
        checkbox.type = "checkbox";
        checkbox.value = color.id;

        const colorSquare = document.createElement("div");
        colorSquare.style.width = "15px";
        colorSquare.style.height = "15px";
        colorSquare.style.backgroundColor = color.hex;
        colorSquare.style.margin = "0 5px";
        colorSquare.style.border = "1px solid #000";

        const colorName = document.createElement("span");
        colorName.textContent = color.en_name;

        label.appendChild(checkbox);
        label.appendChild(colorSquare);
        label.appendChild(colorName);

        colorFiltersDiv.appendChild(label);
    });
}

/**
 * Заполняет фильтр по типам
 * @param {IDBDatabase} db - Открытая база данных
 * @returns {Promise<void>}
 */
export async function populateTypeFilters(db) {
    const types = await getAllRecords(db, "types");
    const typeFiltersDiv = document.getElementById("type-filters");

    types.forEach(type => {
        const label = document.createElement("label");
        label.classList.add("filter-option");
        label.innerHTML = `
            <input type="checkbox" value="${type.id}" /> ${type.en_name}
        `;
        typeFiltersDiv.appendChild(label);
    });
}

/**
 * Заполняет фильтр по языкам
 * @param {IDBDatabase} db - Открытая база данных
 * @returns {Promise<void>}
 */
export async function populateLanguageFilters(db) {
    const languages = await getAllRecords(db, "languages");
    const languageFiltersDiv = document.getElementById("language-filters");

    languagesMap = languages.reduce((map, language) => {
        map[language.id] = language.en_name || "Unknown Language";
        return map;
    }, {});

    languages.forEach(language => {
        const label = document.createElement("label");
        label.classList.add("filter-option");
        label.innerHTML = `
            <input type="checkbox" value="${language.id}" /> ${language.en_name}
        `;
        languageFiltersDiv.appendChild(label);
    });
}

/**
 * Отображает данные в виде таблицы (не меняется)
 * @param {Array} data - Массив данных
 * @param {Array} fields - Поля для отображения
 */
export function displayData(data, fields) {
    const output = document.getElementById("output");
    output.innerHTML = ""; // Очищаем предыдущий вывод

    if (data.length === 0) {
        output.innerHTML = "<p>No data found.</p>";
        return;
    }

    const table = document.createElement("table");
    const thead = document.createElement("thead");
    const tbody = document.createElement("tbody");

    // Создаём заголовок таблицы
    const headerRow = document.createElement("tr");
    fields.forEach(field => {
        const th = document.createElement("th");
        th.textContent = field;
        headerRow.appendChild(th);
    });
    thead.appendChild(headerRow);

    // Создаём строки данных
    data.forEach(item => {
        const row = document.createElement("tr");
        fields.forEach(field => {
            const td = document.createElement("td");
            let value = item[field];
            if (Array.isArray(value)) {
                value = value.join(", ");
            } else if (typeof value === 'object' && value !== null) {
                value = JSON.stringify(value);
            }
            td.textContent = value || "-";
            row.appendChild(td);
        });
        tbody.appendChild(row);
    });

    table.appendChild(thead);
    table.appendChild(tbody);
    output.appendChild(table);
}

// utils.js - Включает вспомогательные функции, такие как debounce и lazy loading.

/**
 * Функция debounce, которая ограничивает частоту вызовов функции
 * @param {Function} func - Функция для обертки
 * @param {number} delay - Задержка в миллисекундах
 */
export function debounce(func, delay) {
    let timeout;
    return function (...args) {
        clearTimeout(timeout);
        timeout = setTimeout(() => func.apply(this, args), delay);
    };
}

/**
 * Показывает загрузочный спиннер
 */
export function showLoading() {
    const spinner = document.getElementById("loading-spinner");
    if (spinner) {
        spinner.style.display = "block";
    }
}

/**
 * Скрывает загрузочный спиннер
 */
export function hideLoading() {
    const spinner = document.getElementById("loading-spinner");
    if (spinner) {
        spinner.style.display = "none";
    }
}

// events.js - Содержит все обработчики событий и назначение слушателей.
import { selectedFilters } from './db.js';
import { handleViewData, paginationState, viewFlagsAsCards } from './main.js';
import { debounce } from './utils.js';

/**
 * Добавляет обработчики событий для фильтров
 */
export function addFilterEventListeners() {
    // Фильтр по цветам
    const colorCheckboxes = document.querySelectorAll("#color-filters input[type='checkbox']");
    colorCheckboxes.forEach(checkbox => {
        checkbox.addEventListener("change", (event) => {
            if (event.target.checked) {
                selectedFilters.colors.add(event.target.value);
            } else {
                selectedFilters.colors.delete(event.target.value);
            }
            handleViewData();
        });
    });

    // Фильтр по типам
    const typeCheckboxes = document.querySelectorAll("#type-filters input[type='checkbox']");
    typeCheckboxes.forEach(checkbox => {
        checkbox.addEventListener("change", (event) => {
            if (event.target.checked) {
                selectedFilters.types.add(event.target.value);
            } else {
                selectedFilters.types.delete(event.target.value);
            }
            handleViewData();
        });
    });

    // Фильтр по языкам
    const languageCheckboxes = document.querySelectorAll("#language-filters input[type='checkbox']");
    languageCheckboxes.forEach(checkbox => {
        checkbox.addEventListener("change", (event) => {
            if (event.target.checked) {
                selectedFilters.languages.add(event.target.value);
            } else {
                selectedFilters.languages.delete(event.target.value);
            }
            handleViewData();
        });
    });
}

/**
 * Добавляет обработчики событий для различных кнопок и элементов
 */
export function setupEventListeners() {
    // Кнопка Поиска
    document.getElementById("search-button").addEventListener("click", async () => {
        handleViewData();
    });

    // Кнопка Очистки Поиска
    document.getElementById("clear-search").addEventListener("click", async () => {
        document.getElementById("search-input").value = "";
        handleViewData();
    });

    // Кнопка Применения Фильтров
    document.getElementById("apply-filters").addEventListener("click", async () => {
        handleViewData();
    });

    // Кнопка Очистки Фильтров
    document.getElementById("clear-filters").addEventListener("click", async () => {
        // Снимаем все чекбоксы
        const allCheckboxes = document.querySelectorAll("#filters input[type='checkbox']");
        allCheckboxes.forEach(checkbox => {
            checkbox.checked = false;
        });

        // Очищаем выбранные фильтры
        selectedFilters.colors.clear();
        selectedFilters.types.clear();
        selectedFilters.languages.clear();

        handleViewData();
    });
}

/**
 * Настраивает реализацию фильтрации в реальном времени
 * @param {IDBDatabase} db - Открытая база данных
 */
export function setupRealTimeFiltering(db) {
    const searchInput = document.getElementById("search-input");

    searchInput.addEventListener("input", debounce(() => {
        // Сброс состояния пагинации
        paginationState.currentPage = 1;
        paginationState.hasMore = true;
        handleViewData(db);
    }, 300));
}

/**
 * Добавляет обработчик для кнопки переключения фильтров
 */
export function setupFilterToggle() {
    const toggleButton = document.getElementById("toggle-filters");
    const filtersPanel = document.getElementById("filters-panel");

    toggleButton.addEventListener("click", () => {
        filtersPanel.classList.toggle("hidden");
    });
}

/**
 * Обработчик события прокрутки для Infinite Scrolling с debounce
 * @param {IDBDatabase} db - Открытая база данных
 */
export function setupInfiniteScroll(db) {
    window.addEventListener("scroll", debounce(async () => {
        if (paginationState.isLoading || !paginationState.hasMore) return;

        if (document.getElementById("output").getBoundingClientRect().bottom <= window.innerHeight * 4) {
            paginationState.isLoading = true;
            paginationState.currentPage += 1;

            const { currentPage, pageSize, filteredFlags } = paginationState;
            const startIndex = (currentPage - 1) * pageSize;
            const endIndex = startIndex + pageSize;
            const flagsToDisplay = filteredFlags.slice(startIndex, endIndex);

            if (flagsToDisplay.length === 0) {
                paginationState.hasMore = false;
                return;
            }

            await viewFlagsAsCards(db, "", selectedFilters, paginationState.currentPage, paginationState.pageSize);
            paginationState.isLoading = false;

            // Проверяем, есть ли ещё флаги для загрузки
            if (endIndex >= filteredFlags.length) {
                paginationState.hasMore = false;
            }
        }
        console.log("Scroll event handled");

    }, 200), { passive: true }); // Опция passive улучшает производительность скроллинга
}

// db.js - Отвечает за инициализацию и взаимодействие с IndexedDB.

import { languageData } from './data/languageData.js';
import { typeData } from './data/typeData.js';
import { colorData } from './data/colorData.js';
import { categoryData } from './data/categoryData.js';
import { flagNameData } from './data/flagNameData.js';
import { flagData } from './data/flagData.js';
import { flagSubdivisionsData } from './data/flagSubdivisionsData.js';
import { flagSubdivisionNamesData } from './data/flagSubdivisionNamesData.js';

const dataMapping = {
    languages: {
        data: languageData,
        storeName: "languages",
        keyPath: "id",
        fields: ["id", "ru_name", "en_name", "zh_name"]
    },
    types: {
        data: typeData,
        storeName: "types",
        keyPath: "id",
        fields: ["id", "ru_name", "en_name", "zh_name"]
    },
    colors: {
        data: colorData,
        storeName: "colors",
        keyPath: "id",
        fields: ["id", "ru_name", "en_name", "zh_name", "hex"]
    },
    categories: {
        data: categoryData,
        storeName: "categories",
        keyPath: "id",
        fields: ["id", "ru_name", "en_name", "zh_name"]
    },
    flagNames: {
        data: flagNameData,
        storeName: "flagNames",
        keyPath: ["flag_id", "language_id"], // Составной ключ
        fields: ["flag_id", "language_id", "short_name", "long_name"]
    },
    flags: {
        data: flagData,
        storeName: "flags",
        keyPath: "id",
        fields: ["id", "is_index_ISO", "resolution", "resolution_ratio", "colors", "types", "originalNames", "parent_flag_id"]
    },
    flagSubdivisions: {
        data: flagSubdivisionsData,
        storeName: "flagSubdivisions",
        keyPath: "id",
        fields: ["category_id", "id", "parent_flag_id"]
    },
    flagSubdivisionNames: {
        data: flagSubdivisionNamesData,
        storeName: "flagSubdivisionNames",
        keyPath: "id",
        fields: ["id", "originalNames", "en_name", "ru_name", "zh_name"]
    },
    // Добавьте больше соответствий при необходимости
};

// Объект для отслеживания выбранных фильтров
export const selectedFilters = {
    colors: new Set(),
    types: new Set(),
    languages: new Set()
};

/**
 * Инициализирует IndexedDB и загружает данные при необходимости
 * @returns {Promise<IDBDatabase>}
 */
export function initDB() {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("FlagsDatabase", 1);

        request.onupgradeneeded = function(event) {
            const db = event.target.result;

            // Создаём объектные хранилища и индексы
            Object.values(dataMapping).forEach(config => {
                if (!db.objectStoreNames.contains(config.storeName)) {
                    if (Array.isArray(config.keyPath)) {
                        const store = db.createObjectStore(config.storeName, { keyPath: config.keyPath });
                        // Создаём индексы, если необходимо
                        if (config.storeName === "flagNames") {
                            store.createIndex("flag_id", "flag_id", { unique: false });
                            store.createIndex("language_id", "language_id", { unique: false });
                        }
                        // Добавьте другие индексы при необходимости
                    } else {
                        const store = db.createObjectStore(config.storeName, { keyPath: config.keyPath });
                        // Создаём индексы
                        if (config.storeName === "languages") {
                            store.createIndex("en_name", "en_name", { unique: false });
                            store.createIndex("ru_name", "ru_name", { unique: false });
                            store.createIndex("zh_name", "zh_name", { unique: false });
                        } else if (config.storeName === "flags") {
                            store.createIndex("types", "types", { unique: false, multiEntry: true });
                            store.createIndex("colors", "colors", { unique: false, multiEntry: true });
                            store.createIndex("parent_flag_id", "parent_flag_id", { unique: false });
                        } else if (config.storeName === "flagSubdivisions") {
                            store.createIndex("parent_flag_id", "parent_flag_id", { unique: false });
                        }
                        // Добавьте другие индексы для других хранилищ при необходимости
                    }
                }
            });
        };

        request.onsuccess = function(event) {
            const db = event.target.result;
            console.log("База данных успешно открыта");
            resolve(db);
        };

        request.onerror = function(event) {
            console.error("Не удалось открыть базу данных", event.target.error);
            reject(event.target.error);
        };
    });
}

/**
 * Добавляет данные в указанное объектное хранилище
 * @param {IDBDatabase} db - Открытая база данных
 * @param {string} storeName - Имя объектного хранилища
 * @param {Array} dataArray - Массив данных для добавления
 * @param {string|Array} keyPath - Ключевое поле или составной ключ
 * @returns {Promise<void>}
 */
export function addData(db, storeName, dataArray, keyPath) {
    return new Promise((resolve, reject) => {
        const transaction = db.transaction([storeName], "readwrite");
        const store = transaction.objectStore(storeName);

        transaction.oncomplete = function() {
            console.log(`Все данные добавлены в ${storeName}`);
            resolve();
        };

        transaction.onerror = function(event) {
            console.error(`Ошибка при добавлении данных в ${storeName}:`, event.target.error);
            reject(event.target.error);
        };

        dataArray.forEach(item => store.put(item));
    });
}

/**
 * Получает количество записей в указанном объектном хранилище
 * @param {IDBDatabase} db - Открытая база данных
 * @param {string} storeName - Имя объектного хранилища
 * @returns {Promise<number>}
 */
export function getCount(db, storeName) {
    return new Promise((resolve, reject) => {
        const transaction = db.transaction([storeName], "readonly");
        const store = transaction.objectStore(storeName);
        const request = store.count();

        request.onsuccess = function(event) {
            resolve(event.target.result);
        };

        request.onerror = function(event) {
            console.error(`Ошибка при подсчёте записей в ${storeName}:`, event.target.error);
            reject(event.target.error);
        };
    });
}

/**
 * Загружает все данные в IndexedDB при необходимости
 * Данные загружаются только при первом создании базы данных
 * @param {IDBDatabase} db - Открытая база данных
 * @returns {Promise<void>}
 */
export async function loadData(db) {
    try {
        // Проверяем, пусто ли объектное хранилище "languages"
        const countLanguages = await getCount(db, "languages");
        console.log(`Количество записей в 'languages': ${countLanguages}`);

        if (countLanguages === 0) {
            for (const [key, config] of Object.entries(dataMapping)) {
                console.log(`Добавление данных в хранилище: ${config.storeName}`);
                await addData(db, config.storeName, config.data, config.keyPath);
                console.log(`Данные добавлены в хранилище: ${config.storeName}`);
            }
            console.log("Все данные успешно загружены в IndexedDB.");
        } else {
            console.log("Данные уже существуют в базе данных. Загрузка не требуется.");
        }
    } catch (error) {
        console.error("Произошла ошибка при загрузке данных. Пожалуйста, попробуйте позже.", error);
    }
}

/**
 * Получает все записи из указанного объектного хранилища
 * @param {IDBDatabase} db - Открытая IndexedDB база данных
 * @param {string} storeName - Имя объектного хранилища
 * @returns {Promise<Array>}
 */
export function getAllRecords(db, storeName) {
    return new Promise((resolve, reject) => {
        const transaction = db.transaction([storeName], "readonly");
        const store = transaction.objectStore(storeName);
        const request = store.getAll();

        request.onsuccess = function(event) {
            resolve(event.target.result);
        };

        request.onerror = function(event) {
            console.error(`Ошибка при получении данных из ${storeName}:`, event.target.error);
            reject(event.target.error);
        };
    });
}

Модель данных (на самом деле данных больше, это пример):
const languageData = [
{ id: "aa", ru_name: "афарский", en_name: "Afar", zh_name: "阿法尔语" },
{ id: "ab", ru_name: "абхазский", en_name: "Abkhazian", zh_name: "阿布哈兹语" },
];

const typeData = [
{ id: "2", ru_name: "Африка", en_name: "Africa", zh_name: "阿非利加洲" },
{ id: "3", ru_name: "Европа", en_name: "Europe", zh_name: "欧洲" },
];

const colorData = [
{ id: "1", ru_name: "пудровый", en_name: "powder", zh_name: "蜜桃色", hex: "#FFE2BD" },
{ id: "2", ru_name: "розовый", en_name: "pink", zh_name: "浅珍珠红色", hex: "#ED72AA" },
];


const categoryData = [
	{ id: "1", en_name: "European collectivity", ru_name: "европейское территориальное образование", zh_name: "欧洲集体" },
	{ id: "2", en_name: "Land", ru_name: "земля", zh_name: "联邦州" },
];

const flagNameData = [
{ flag_id: "AB", language_id: "en", short_name: "Ambazonia", long_name: "Federal Republic of Ambazonia" },
{ flag_id: "AB", language_id: "ru", short_name: "Амбазония", long_name: "Федеративная Республика Амбазония" },
{ flag_id: "AB", language_id: "zh", short_name: "安巴佐尼亚", long_name: "安巴佐尼亚联邦共和国" },
{ flag_id: "AC", language_id: "en", short_name: "Ascension Island", long_name: '' },
{ flag_id: "AC", language_id: "ru", short_name: "Остров Вознесения", long_name: '' },
{ flag_id: "AC", language_id: "zh", short_name: "阿森松岛", long_name: '' },
];

const flagData = [
	{
		id: "AB",
		is_index_ISO: false,
		resolution: "2:3",
		resolution_ratio: 0.67,
		colors: ["11", "15", "16", "21", "24", "7"],
		types: ["10", "2"],
		originalNames: [
			{
				language_id: "en",
				original_name: "Federal Republic of Ambazonia",
			},
		],
	},
	{
		id: "AC",
		is_index_ISO: true,
		resolution: "1:2",
		resolution_ratio: 0.5,
		colors: [
			"1",
			"10",
			"11",
			"12",
			"13",
			"14",
			"16",
			"18",
			"19",
			"21",
			"22",
			"23",
			"24",
			"5",
			"6",
			"7",
			"8",
			"9",
		],
		types: ["11", "12", "2"],
		originalNames: [
			{
				language_id: "en",
				original_name: "Ascension Island",
			},
		],
	},
];

const flagSubdivisionsData = [
    {
        category_id: "150", 
        id: "AD-02",
        parent_flag_id: "AD",
    },
    {
        category_id: "150", 
        id: "AD-03",
        parent_flag_id: "AD",
    },
];

const flagSubdivisionNamesData = [
    {
        "id": "AD-02",
        "originalNames": [
            {
                "language_id": "ca",
                "original_name": "Canillo"
            }
        ],
        "en_name": "Canillo",
        "ru_name": "Канильо",
        "zh_name": "卡尼略"
    },
    {
        "id": "AD-03",
        "originalNames": [
            {
                "language_id": "ca",
                "original_name": "Encamp"
            }
        ],
        "en_name": "Encamp",
        "ru_name": "Энкам",
        "zh_name": "恩坎普"
    },
];