/*!
 * Custom elements for Nanopore-Digital Web applications
 */
import { createStore } from "@stencil/store";
import { cyrb53 } from "../../utils/math";
import { createWorkerStore, syncConfigToWorker, syncDataToWorker, workerFilter, workerSearch, workerSort, } from "./table.worker";
import { getNestedProperty } from "./table.utils";
function colsToWorker(columns) {
    const safeColumns = JSON.parse(JSON.stringify(columns));
    columns.forEach((c) => {
        if (c?.sortCompareFn) {
            const safeCol = safeColumns.find((sc) => sc.prop === c.prop);
            safeCol.sortCompareFn = c.sortCompareFn.toString();
        }
    });
    return safeColumns;
}
const stores = new WeakMap();
export async function generateStore(host, columns, scrollParent, isReady) {
    const store = {
        data: createStore({ rows: [] }),
        config: createStore({ columns }),
        general: createStore({ workerId: null, scrollParent, host, isReady }),
    };
    const id = await createWorkerStore(store.data.state.rows, colsToWorker(store.config.state.columns));
    store.general.state.workerId = id;
    stores.set(host, store);
    // sync data from our store to our worker
    store.data.use({
        reset: () => {
            if (store.general.state.workerId)
                syncDataToWorker(store.general.state.workerId, null);
        },
        dispose: () => {
            if (store.general.state.workerId)
                syncDataToWorker(store.general.state.workerId, null);
        },
    });
    store.config.use({
        reset: () => {
            if (store.general.state.workerId)
                syncConfigToWorker(store.general.state.workerId, null);
        },
        dispose: () => {
            if (store.general.state.workerId)
                syncConfigToWorker(store.general.state.workerId, null);
        },
    });
    return store;
}
export function getStore(host) {
    return stores.get(host);
}
function getDataType(value) {
    if (value instanceof Date) {
        return 'date';
    }
    if (['number', 'string', 'boolean'].includes(typeof value)) {
        return typeof value;
    }
    return 'unknown';
}
export function storeSetData(host, rows) {
    const store = stores.get(host);
    if (!store)
        return;
    const cols = store.config.state.columns;
    const dateCols = cols
        .filter((col) => col.type === 'date')
        .reduce((prev, curr) => {
        return [...prev, curr.prop];
    }, []);
    const unknownCols = cols.filter((col) => !col.type);
    // augments data with some internal props.
    // discern unknown column types
    // convert dates to numbers for worker
    rows = rows.map((row, i) => {
        // try our best to discern the column type (from first row) if unset
        if (unknownCols.length && i === 0) {
            store.config.state.columns = cols.map((col) => {
                const val = getNestedProperty(row, col.prop);
                if (unknownCols.includes(col) && val && !col.type) {
                    col.type = getDataType(val);
                    if (col.type === 'date')
                        dateCols.push(col.prop);
                }
                return col;
            });
        }
        // convert date columns into numbers to send to our worker
        dateCols.forEach((colName) => {
            // coerce any date type;
            // Date(), timestamp, valid date string
            const cellValue = getNestedProperty(row, colName);
            const coerceDate = new Date(cellValue);
            if (!!coerceDate && Number(coerceDate)) {
                // Store as a flattened property (e.g., row["user.birthDate"])
                // getNestedProperty will check this first before traversing nested structure
                row[colName] = Number(coerceDate);
            }
        });
        row['__index'] = i;
        row['__uuid'] = cyrb53(cols.map((c) => getNestedProperty(row, c.prop)).join());
        return row;
    });
    store.data.state.rows = rows;
    if (store.general.state.workerId)
        return syncDataToWorker(store.general.state.workerId, rows);
}
export function storeSetConfig(host, columns) {
    const store = stores.get(host);
    if (!store)
        return;
    store.config.state.columns = columns;
    if (store.general.state.workerId)
        return syncConfigToWorker(store.general.state.workerId, colsToWorker(columns));
}
export async function storeSearch(host, term) {
    const store = stores.get(host);
    if (!store || !store.general.state.workerId)
        return;
    try {
        store.data.state.rows = await workerSearch(store.general.state.workerId, term);
    }
    catch (e) {
        console.warn(e);
    }
}
export async function storeFilter(host, filters) {
    const store = stores.get(host);
    if (!store || !store.general.state.workerId)
        return;
    try {
        store.data.state.rows = await workerFilter(store.general.state.workerId, filters);
    }
    catch (e) {
        console.warn(e);
    }
}
export async function storeSort(host, prop, order) {
    const store = stores.get(host);
    if (!store || !store.general.state.workerId)
        return;
    try {
        store.data.state.rows = await workerSort(store.general.state.workerId, prop, order);
    }
    catch (e) {
        console.warn(e);
    }
}
