sort function const API_URL = '/data.json'
const HEADER = document.querySelector('header')
const ACTIONS_CONTAINER = document.querySelector('.actions-container')
const FORM = document.querySelector('#form')
const TABLE = document.querySelector('#table')

let LAST_SAVED_DATA = [];
let UI_DATA = [];
let IS_UNSAVED_CHANGES = false;
const SELECTED_ROW = { data: null, html: null }

const OBSERVER = new MutationObserver(function () {
    IS_UNSAVED_CHANGES = true
});
const OBSERVER_CONFIG = { attributes: false, childList: true, subtree: true }

async function fetchData(url) {
    try {
        const res = await fetch(url);
        const json = await res.json();
        return json
    } catch (e) {
        console.log(e)
        return []
    }
}

function getUIDataItemIndex(id) {
    return UI_DATA.findIndex(el => el.id === id)
}

function swapArrayItems(arr, index1, index2) {
    const temp = arr[index1]
    arr[index1] = arr[index2]
    arr[index2] = temp
}

function swapRowIndex(row1, row2) {
    const index1 = row1.children[1].textContent
    const index2 = row2.children[1].textContent

    row1.children[1].textContent = index2
    row2.children[1].textContent = index1
}

function saveData() {
    LAST_SAVED_DATA = JSON.parse(JSON.stringify(UI_DATA))
}

function resetSelectionData() {
    SELECTED_ROW.data = null
    SELECTED_ROW.html = null
}

function refreshUIData() {
    UI_DATA = JSON.parse(JSON.stringify(LAST_SAVED_DATA))
}

function sortUIData(property, dir, type) {
    UI_DATA.sort((a, b) => {
        if (type === 'string') {
            if (dir === 'asc') {
                return a[property].localeCompare(b[property], undefined, { numeric: true });
            } else {
                return b[property].localeCompare(a[property], undefined, { numeric: true });
            }
        } else if (type === 'number') {
            if (dir === 'asc') {
                return a[property] - b[property];
            } else {
                return b[property] - a[property];
            }
        } else {
            console.log(`Unsupported type: ${type}`);
            return 0;
        }
    });
}

function populateForm(data) {
    FORM.chemical_name.value = data.chemical_name;
    FORM.vendor.value = data.vendor;
    FORM.density.value = data.density;
    FORM.viscosity.value = data.viscosity;
    FORM.packaging.value = data.packaging;
    FORM.pack_size.value = data.pack_size;
    FORM.unit.value = data.unit;
    FORM.quantity.value = data.quantity;
}

function toggleForm() {
    HEADER.classList.toggle('show-form')
}

function createCell(text) {
    const cell = document.createElement('td');
    cell.textContent = text;
    return cell
}

function createRow(obj, index) {
    const row = document.createElement('tr');

    const selectBox = createCell(`\u2714`);
    selectBox.classList.add('select-box');
    row.appendChild(selectBox);

    row.dataset.id = obj['id']
    const rowIndexCell = createCell(index);
    row.appendChild(rowIndexCell)

    for (const key in obj) {
        if (key === 'id') {
            continue
        }

        const cell = createCell(obj[key])
        cell.dataset.property = key
        row.appendChild(cell);
    }

    return row
}

function createTableBody(arrayOfObjs) {
    const fragment = document.createDocumentFragment();

    const tbody = document.createElement('tbody');
    fragment.appendChild(tbody)

    arrayOfObjs.forEach((obj, index) => {
        const row = createRow(obj, index + 1)
        tbody.appendChild(row);
    });
    return fragment
}

function selectRow(row) {
    const clickedRow = row

    removeAllModes()

    if (clickedRow.classList.contains('active')) {
        clickedRow.classList.remove('active')

        resetSelectionData()
        idealMode()
    } else {
        const allRows = TABLE.lastElementChild.children
        for (let i = 0; i < allRows.length; i++) {
            allRows[i].classList.remove('active')
        }
        clickedRow.classList.add('active')
        SELECTED_ROW.data = UI_DATA[getUIDataItemIndex(clickedRow.dataset.id)]

        SELECTED_ROW.html = clickedRow
        selectedMode()
    }
}

function deleteRow() {
    if (SELECTED_ROW.data === null || SELECTED_ROW.html === null) {
        return
    }

    // ui
    let currentRow = SELECTED_ROW.html;
    let currentRowIndex = parseInt(currentRow.children[1].textContent);
    while (currentRow) {
        currentRow.children[1].textContent = currentRowIndex - 1
        currentRowIndex++
        currentRow = currentRow.nextElementSibling
    }

    SELECTED_ROW.html.remove();

    // data
    const deletedItemDataIndex = getUIDataItemIndex(SELECTED_ROW.data.id)
    UI_DATA.splice(deletedItemDataIndex, 1)

    resetSelectionData()
}

function moveRowUp() {
    if (SELECTED_ROW.data === null || SELECTED_ROW.html === null) {
        return
    }

    const currentRow = SELECTED_ROW.html
    const previousRow = currentRow.previousElementSibling;

    if (previousRow) {
        const previousRowDataId = getUIDataItemIndex(previousRow.dataset.id)
        currentRow.parentNode.insertBefore(currentRow, previousRow);
        swapRowIndex(previousRow, currentRow)
        swapArrayItems(UI_DATA, previousRowDataId, previousRowDataId + 1)
    }
}

function moveRowDown() {
    if (SELECTED_ROW.data === null || SELECTED_ROW.html === null) {
        return
    }

    const currentRow = SELECTED_ROW.html
    const nextRow = currentRow.nextElementSibling;

    if (nextRow) {
        const nextItemDataIndex = getUIDataItemIndex(nextRow.dataset.id)
        currentRow.parentNode.insertBefore(nextRow, currentRow);
        swapRowIndex(nextRow, currentRow)
        swapArrayItems(UI_DATA, nextItemDataIndex, nextItemDataIndex - 1)
    }
}

function renderTableUI(data) {
    const html = createTableBody(data)
    TABLE.lastElementChild.remove()
    TABLE.append(html)
}

function refresh() {
    if (IS_UNSAVED_CHANGES) {
        const userConfimation = window.confirm('Contains Unsaved Changes, Refresh?')

        if (!userConfimation) return
    }

    OBSERVER.disconnect()
    refreshUIData()
    renderTableUI(UI_DATA)
    removeAllModes()
    idealMode()
    IS_UNSAVED_CHANGES = false
    OBSERVER.observe(TABLE, OBSERVER_CONFIG)
}

function save() {
    saveData()
    IS_UNSAVED_CHANGES = false
}

function editForm() {
    FORM.dataset.mode = 'edit'
    toggleForm()
    populateForm(SELECTED_ROW.data)
}

function removeAllModes() {
    HEADER.classList.remove('show-toggle-form', 'show-edit', 'show-delete', 'show-moveup', 'show-movedown', 'show-refresh', 'show-save')
}

function idealMode() {
    HEADER.classList.add('show-toggle-form', 'show-refresh', 'show-save')
}

function selectedMode() {
    HEADER.classList.add('show-edit', 'show-delete', 'show-moveup', 'show-movedown', 'show-refresh', 'show-save')
}

function formMode() {
    HEADER.classList.add('show-toggle-form')
}

// init
fetchData(API_URL)
    .then(res => {
        const resStr = JSON.stringify(res)
        LAST_SAVED_DATA = JSON.parse(resStr);
        UI_DATA = JSON.parse(resStr);

        const html = createTableBody(UI_DATA)
        TABLE.append(html)

        TABLE.addEventListener('click', function (e) {

            const clickedArea = e.target;

            if (clickedArea.classList.contains('select-box')) {
                selectRow(clickedArea.parentElement)
            }

            // sorting
            if (clickedArea.dataset.sortProperty !== undefined) {
                const direction = clickedArea.dataset.sortDirection
                sortUIData(clickedArea.dataset.sortProperty, direction, clickedArea.dataset.sortType)
                clickedArea.dataset.sortDirection = direction === "asc" ? "dsc" : "asc"

                resetSelectionData()
                renderTableUI(UI_DATA)
            }
        });

        ACTIONS_CONTAINER.addEventListener('click', function (e) {
            const button = e.target.closest('button');
            if (!button) return;

            const action = e.target.dataset.action;

            switch (action) {
                case 'edit':
                    editForm()
                    break

                case 'toggle-form':
                    toggleForm()
                    break

                case 'moveup':
                    moveRowUp()
                    break

                case 'movedown':
                    moveRowDown()
                    break

                case 'delete':
                    deleteRow()
                    break

                case 'refresh':
                    refresh()
                    break

                case 'save':
                    save()
                    break

                default:
                    return
            }
        })

        FORM.addEventListener('submit', function (e) {
            e.preventDefault()
            const formData = new FormData(e.target)
            const data = Object.fromEntries(formData.entries())

            if (FORM.dataset.mode === 'edit') {
                const id = SELECTED_ROW.data.id
                UI_DATA[getUIDataItemIndex(id)] = { id: id, ...data }
                FORM.dataset.mode = 'add'
            } else {
                const id = `${new Date().getTime()}`
                UI_DATA.push({ id: id, ...data })
            }

            FORM.reset()
            toggleForm()

            renderTableUI(UI_DATA)
            resetSelectionData()
            removeAllModes()
            idealMode()
        });

        // changes observer
        OBSERVER.observe(TABLE, OBSERVER_CONFIG);

    }).catch(function (e) {
        console.log(e)
    })