// Mapping of data types to their corresponding data arrays
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"]
    },
    flagNames: {
        data: flagNameData,
        storeName: "flagNames",
        keyPath: ["flag_id", "language_id"], // Composite key
        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"]
    },
    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"]
    },
    // Add more mappings as needed
};

// Function to initialize the IndexedDB
function initDB() {
    return new Promise((resolve, reject) => {
        const request = indexedDB.open("FlagsDatabase", 1);

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

            // Iterate through each data type and create object stores
            Object.values(dataMapping).forEach(config => {
                if (!db.objectStoreNames.contains(config.storeName)) {
                    // Handle composite key for flagNames
                    if (Array.isArray(config.keyPath)) {
                        const store = db.createObjectStore(config.storeName, { keyPath: config.keyPath });
                        // Optionally, create indexes here
                        // Example:
                        // store.createIndex("flag_id", "flag_id", { unique: false });
                    } else {
                        const store = db.createObjectStore(config.storeName, { keyPath: config.keyPath });
                        // Optionally, create indexes here
                        // Example:
                        // store.createIndex("en_name", "en_name", { unique: false });
                    }
                }
            });
        };

        request.onsuccess = function(event) {
            const db = event.target.result;
            console.log("Database initialized");
            resolve(db);
        };

        request.onerror = function(event) {
            console.error("Database failed to open", event.target.error);
            reject(event.target.error);
        };
    });
}

// Function to add data to a specific object store
function addData(db, storeName, dataArray, keyPath) {
    return new Promise((resolve, reject) => {
        const transaction = db.transaction([storeName], "readwrite");
        const store = transaction.objectStore(storeName);
        const hasKeyPath = store.keyPath !== null; // Check if the store has a keyPath

        dataArray.forEach(item => {
            if (hasKeyPath) {
                // In-line key: key is part of the object, do not pass the key separately
                store.put(item);
            } else {
                // Out-of-line key: pass the key as the second parameter
                // Replace 'id' with the appropriate key field if different
                store.put(item, item.id);
            }
        });

        transaction.oncomplete = function() {
            console.log(`All data added to ${storeName}`);
            resolve();
        };

        transaction.onerror = function(event) {
            console.error(`Error adding data to ${storeName}:`, event.target.error);
            reject(event.target.error);
        };
    });
}

// Function to load all data
async function loadData(db) {
    try {
        for (const [key, config] of Object.entries(dataMapping)) {
            await addData(db, config.storeName, config.data, config.keyPath);
        }
        alert("Data successfully loaded into IndexedDB!");
    } catch (error) {
        console.error("Error loading data:", error);
    }
}

// Function to fetch and display data based on selected type and search query
function viewData(db, storeName, fields, searchQuery = "") {
    const output = document.getElementById("output");
    output.innerHTML = ""; // Clear previous content

    if (storeName === "flags") {
        // Special handling for flags: display as cards
        viewFlagsAsCards(db);
        return;
    }
    
    const transaction = db.transaction([storeName], "readonly");
    const store = transaction.objectStore(storeName);
    let request;

    if (searchQuery) {
        // For simplicity, perform a basic filter on the first field (e.g., 'id' or 'en_name')
        // More advanced search would require indexing and querying accordingly
        const results = [];
        store.openCursor().onsuccess = function(event) {
            const cursor = event.target.result;
            if (cursor) {
                const record = cursor.value;
                const firstField = fields[0];
                if (record[firstField] && record[firstField].toString().toLowerCase().includes(searchQuery.toLowerCase())) {
                    results.push(record);
                }
                cursor.continue();
            } else {
                displayData(results, fields);
            }
        };
    } else {
        request = store.getAll();

        request.onsuccess = function(event) {
            const data = event.target.result;
            displayData(data, fields);
        };

        request.onerror = function(event) {
            console.error("Error fetching data:", event.target.error);
        };
    }
}

// Utility function to display data in a table
function displayData(data, fields) {
    const output = document.getElementById("output");
    output.innerHTML = ""; // Clear previous content

    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");

    // Create header row
    const headerRow = document.createElement("tr");
    fields.forEach(field => {
        const th = document.createElement("th");
        th.textContent = field;
        headerRow.appendChild(th);
    });
    thead.appendChild(headerRow);

    // Create data rows
    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);
}

// Function to create dynamic form fields based on selected data type
function createFormFields(fields, record = {}) {
    const form = document.getElementById("record-form");
    form.innerHTML = ""; // Clear existing form fields

    fields.forEach(field => {
        // Skip fields that are arrays or objects for simplicity
        // Advanced implementation can handle nested structures
        if (typeof record[field] === "object" && record[field] !== null) {
            return;
        }

        const label = document.createElement("label");
        label.textContent = `${field}: `;
        const input = document.createElement("input");
        input.type = "text";
        input.name = field;
        input.value = record[field] || "";
        label.appendChild(input);
        form.appendChild(label);
        form.appendChild(document.createElement("br"));
    });
}

// Function to show the form
function showForm(mode, config, record = {}) {
    const formContainer = document.getElementById("form-container");
    const formTitle = document.getElementById("form-title");
    formTitle.textContent = mode === "add" ? "Add Record" : "Update Record";

    createFormFields(config.fields, record);
    formContainer.style.display = "block";

    // Store mode and config in the form's dataset for later use
    const form = document.getElementById("record-form");
    form.dataset.mode = mode;
    form.dataset.storeName = config.storeName;
    form.dataset.keyPath = Array.isArray(config.keyPath) ? config.keyPath.join(",") : config.keyPath;
    // Store the primary key for update/delete operations
    if (mode === "update" || mode === "delete") {
        form.dataset.primaryKey = JSON.stringify(getPrimaryKey(config, record));
    }
}

// Function to hide the form
function hideForm() {
    const formContainer = document.getElementById("form-container");
    const form = document.getElementById("record-form");
    form.innerHTML = "";
    formContainer.style.display = "none";
}

// Function to get primary key based on keyPath
function getPrimaryKey(config, record) {
    if (Array.isArray(config.keyPath)) {
        return config.keyPath.map(k => record[k]);
    } else {
        return record[config.keyPath];
    }
}

// Function to save (add/update) a record
async function saveRecord(db) {
    const form = document.getElementById("record-form");
    const mode = form.dataset.mode;
    const storeName = form.dataset.storeName;
    const keyPath = form.dataset.keyPath.split(",");
    const primaryKey = form.dataset.primaryKey ? JSON.parse(form.dataset.primaryKey) : null;

    // Collect form data
    const formData = new FormData(form);
    const record = {};
    formData.forEach((value, key) => {
        // Attempt to parse numbers and booleans
        if (value === "true") {
            record[key] = true;
        } else if (value === "false") {
            record[key] = false;
        } else if (!isNaN(value)) {
            record[key] = Number(value);
        } else {
            record[key] = value;
        }
    });

    // For composite keys
    let key;
    if (keyPath.length > 1) {
        key = keyPath.map(k => record[k]);
    } else {
        key = record[keyPath[0]];
    }

    try {
        const transaction = db.transaction([storeName], "readwrite");
        const store = transaction.objectStore(storeName);

        if (mode === "add" || mode === "update") {
            await new Promise((resolve, reject) => {
                if (Array.isArray(key)) {
                    store.put(record, key);
                } else {
                    store.put(record);
                }
                transaction.oncomplete = resolve;
                transaction.onerror = () => reject(transaction.error);
            });
            alert(`Record ${mode === "add" ? "added" : "updated"} successfully!`);
        }
    } catch (error) {
        console.error("Error saving record:", error);
    }
}

// Function to delete a record
async function deleteRecord(db) {
    const form = document.getElementById("record-form");
    const storeName = form.dataset.storeName;
    const keyPath = form.dataset.keyPath.split(",");
    const primaryKey = form.dataset.primaryKey ? JSON.parse(form.dataset.primaryKey) : null;

    if (!primaryKey) {
        alert("No record selected for deletion.");
        return;
    }

    try {
        const transaction = db.transaction([storeName], "readwrite");
        const store = transaction.objectStore(storeName);
        const deletePromise = new Promise((resolve, reject) => {
            if (Array.isArray(primaryKey)) {
                store.delete(primaryKey);
            } else {
                store.delete(primaryKey);
            }
            transaction.oncomplete = resolve;
            transaction.onerror = () => reject(transaction.error);
        });

        await deletePromise;
        alert("Record deleted successfully!");
    } catch (error) {
        console.error("Error deleting record:", error);
    }
}

// Event Listeners for Buttons
document.getElementById("init-db").addEventListener("click", async () => {
    try {
        await initDB();
        alert("Database initialized successfully!");
    } catch (error) {
        alert("Failed to initialize database.");
    }
});

document.getElementById("load-data").addEventListener("click", async () => {
    try {
        const db = await initDB();
        await loadData(db);
    } catch (error) {
        alert("Failed to load data.");
    }
});

document.getElementById("view-data").addEventListener("click", async () => {
    const db = await initDB();
    const dataType = document.getElementById("data-type").value;
    const config = dataMapping[dataType];
    const searchQuery = document.getElementById("search-input").value.trim();
    viewData(db, config.storeName, config.fields, searchQuery);
});

// Search Functionality
document.getElementById("search-button").addEventListener("click", async () => {
    const db = await initDB();
    const dataType = document.getElementById("data-type").value;
    const config = dataMapping[dataType];
    const searchQuery = document.getElementById("search-input").value.trim();
    viewData(db, config.storeName, config.fields, searchQuery);
});

// Clear Search
document.getElementById("clear-search").addEventListener("click", async () => {
    document.getElementById("search-input").value = "";
    const db = await initDB();
    const dataType = document.getElementById("data-type").value;
    const config = dataMapping[dataType];
    viewData(db, config.storeName, config.fields);
});

// Add Record
document.getElementById("add-record").addEventListener("click", async () => {
    const dataType = document.getElementById("data-type").value;
    const config = dataMapping[dataType];
    showForm("add", config);
});

// Update Record
document.getElementById("update-record").addEventListener("click", async () => {
    const dataType = document.getElementById("data-type").value;
    const config = dataMapping[dataType];
    const db = await initDB();

    // Prompt user to enter the primary key of the record to update
    let key = prompt(`Enter the primary key (${config.keyPath}) of the record to update:`);

    if (!key) {
        alert("No key entered.");
        return;
    }

    let parsedKey = key;
    if (Array.isArray(config.keyPath)) {
        // For composite keys, expect comma-separated values
        const keys = key.split(",").map(k => k.trim());
        if (keys.length !== config.keyPath.length) {
            alert(`Please enter ${config.keyPath.length} values separated by commas.`);
            return;
        }
        parsedKey = keys;
    }

    // Fetch the record
    let record;
    try {
        const transaction = db.transaction([config.storeName], "readonly");
        const store = transaction.objectStore(config.storeName);
        const request = store.get(parsedKey);
        
        await new Promise((resolve, reject) => {
            request.onsuccess = function(event) {
                record = event.target.result;
                resolve();
            };
            request.onerror = function(event) {
                reject(event.target.error);
            };
        });
    } catch (error) {
        alert("Error fetching the record.");
        return;
    }

    if (!record) {
        alert("Record not found.");
        return;
    }

    showForm("update", config, record);
});

// Delete Record
document.getElementById("delete-record").addEventListener("click", async () => {
    const dataType = document.getElementById("data-type").value;
    const config = dataMapping[dataType];
    const db = await initDB();

    // Prompt user to enter the primary key of the record to delete
    let key = prompt(`Enter the primary key (${config.keyPath}) of the record to delete:`);

    if (!key) {
        alert("No key entered.");
        return;
    }

    let parsedKey = key;
    if (Array.isArray(config.keyPath)) {
        // For composite keys, expect comma-separated values
        const keys = key.split(",").map(k => k.trim());
        if (keys.length !== config.keyPath.length) {
            alert(`Please enter ${config.keyPath.length} values separated by commas.`);
            return;
        }
        parsedKey = keys;
    }

    // Confirm deletion
    if (!confirm("Are you sure you want to delete this record?")) {
        return;
    }

    // Delete the record
    try {
        const transaction = db.transaction([config.storeName], "readwrite");
        const store = transaction.objectStore(config.storeName);
        const deleteRequest = store.delete(parsedKey);

        await new Promise((resolve, reject) => {
            transaction.oncomplete = resolve;
            transaction.onerror = () => reject(transaction.error);
        });

        alert("Record deleted successfully!");
    } catch (error) {
        alert("Error deleting the record.");
    }
});

// Save Record (Add/Update)
document.getElementById("save-record").addEventListener("click", async () => {
    const db = await initDB();
    await saveRecord(db);
    hideForm();
});

// Cancel Record Operation
document.getElementById("cancel-record").addEventListener("click", () => {
    hideForm();
});

// Function to fetch all records from a specific object store
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(`Error fetching data from ${storeName}:`, event.target.error);
            reject(event.target.error);
        };
    });
}

// Function to view flags as cards
async function viewFlagsAsCards(db) {
    try {
        // Fetch all necessary data
        const flags = await getAllRecords(db, "flags");
        const flagNames = await getAllRecords(db, "flagNames");
        const colors = await getAllRecords(db, "colors");

        // Clear previous output
        const output = document.getElementById("output");
        output.innerHTML = "";

        // Iterate through each flag and create a card
        flags.forEach(flag => {
            // Create card element
            const card = document.createElement("div");
            card.className = "flag_card";

            // Flag Index
            const flagIndex = document.createElement("span");
            flagIndex.className = "flag_index";
            flagIndex.textContent = flag.id;
            card.appendChild(flagIndex);

            // Flag Image
            const flagImgDiv = document.createElement("div");
            flagImgDiv.className = "flag_img";
            const img = document.createElement("img");
            img.src = `flags/PNG-1200/PNG-0.5/${flag.id}-0.5-1200.png`; // Adjust the path as needed
            img.alt = `Flag of ${flag.id}`;

            img.onerror = function() {
                this.src = 'https://placehold.co/400'; // Path to your placeholder image
                this.alt = 'Image not available';
            };

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

            // Flag Short Name
            // Assuming 'en_name' from 'flags' store or fetch from flagNameData
            const shortName = document.createElement("div");
            shortName.className = "flag_shortname";
            // Fetch the English short name
            const enNameObj = flagNames.find(fn => fn.flag_id === flag.id && fn.language_id === "en");
            shortName.textContent = enNameObj ? enNameObj.short_name : "N/A";
            card.appendChild(shortName);

            // Flag Long Name
            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);

            // Flag Names in Different Languages
            const namesContainer = document.createElement("div");
            namesContainer.className = "flag_names_container";

            // Filter flagNames for this flag
            const namesForFlag = flagNames.filter(fn => fn.flag_id === flag.id);

            namesForFlag.forEach(nameObj => {
                const langSpan = document.createElement("span");
                langSpan.className = "flag_original_lang";
                langSpan.title = getLanguageTitle(nameObj.language_id);
                langSpan.textContent = nameObj.language_id;
                namesContainer.appendChild(langSpan);

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

            card.appendChild(namesContainer);

            // Flag Colors
            const colorsContainer = document.createElement("div");
            colorsContainer.className = "flag_colors_container";

            flag.colors.forEach(colorId => {
                const colorObj = colors.find(c => c.id === colorId);
                if (colorObj) {
                    const colorDiv = document.createElement("div");
                    colorDiv.className = "flag_color";
                    colorDiv.title = colorObj.ru_name; // Assuming title is the Russian name
                    colorDiv.style.backgroundColor = colorObj.hex;
                    colorsContainer.appendChild(colorDiv);
                }
            });

            card.appendChild(colorsContainer);

            // Append card to output
            output.appendChild(card);
        });

    } catch (error) {
        console.error("Error displaying flag cards:", error);
    }
}

// Helper function to get language title based on language_id
function getLanguageTitle(language_id) {
    const language = languageData.find(lang => lang.id === language_id);
    if (language) {
        // Define titles as needed
        const titles = {
            en: "English",
            ru: "Русский",
            zh: "中文",
            ca: "Catalan",
            md: "Moldovan", // Assuming 'md' is Moldovan
            // Add other languages as needed
        };
        return titles[language_id] || "Unknown Language";
    }
    return "Unknown Language";
}