<script setup>
import { ref, onBeforeMount, onMounted, computed, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router';
import { useQuasar } from 'quasar';
import { useStateStore } from 'stores/useStateStore';

import CrudModel from 'src/components/CrudModel.vue';
import FormModelPopup from 'components/FormModelPopup.vue';

import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
import VnTableColumn from 'components/VnTable/VnColumn.vue';
import VnTableFilter from 'components/VnTable/VnFilter.vue';
import VnTableChip from 'components/VnTable/VnChip.vue';
import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue';
import VnLv from 'components/ui/VnLv.vue';
import VnTableOrder from 'src/components/VnTable/VnOrder.vue';

const $props = defineProps({
    columns: {
        type: Array,
        required: true,
    },
    defaultMode: {
        type: String,
        default: 'table', // 'table', 'card'
    },
    columnSearch: {
        type: Boolean,
        default: true,
    },
    rightSearch: {
        type: Boolean,
        default: true,
    },
    rowClick: {
        type: [Function, Boolean],
        default: null,
    },
    rowCtrlClick: {
        type: [Function, Boolean],
        default: null,
    },
    redirect: {
        type: String,
        default: null,
    },
    create: {
        type: Object,
        default: null,
    },
    createAsDialog: {
        type: Boolean,
        default: true,
    },
    cardClass: {
        type: String,
        default: 'flex-one',
    },
    searchUrl: {
        type: String,
        default: 'table',
    },
    isEditable: {
        type: Boolean,
        default: false,
    },
    useModel: {
        type: Boolean,
        default: false,
    },

    hasSubToolbar: {
        type: Boolean,
        default: null,
    },
    disableOption: {
        type: Object,
        default: () => ({ card: false, table: false }),
    },
    withoutHeader: {
        type: Boolean,
        default: false,
    },
    tableCode: {
        type: String,
        default: null,
    },
    table: {
        type: Object,
        default: () => ({}),
    },
    crudModel: {
        type: Object,
        default: () => ({}),
    },
    tableHeight: {
        type: String,
        default: '90vh',
    },
    footer: {
        type: Boolean,
        default: false,
    },
});
const { t } = useI18n();
const stateStore = useStateStore();
const route = useRoute();
const router = useRouter();
const quasar = useQuasar();

const CARD_MODE = 'card';
const TABLE_MODE = 'table';
const mode = ref(CARD_MODE);
const selected = ref([]);
const hasParams = ref(false);
const routeQuery = JSON.parse(route?.query[$props.searchUrl] ?? '{}');
const params = ref({ ...routeQuery, ...routeQuery.filter?.where });
const orders = ref(parseOrder(routeQuery.filter?.order));
const CrudModelRef = ref({});
const showForm = ref(false);
const splittedColumns = ref({ columns: [] });
const columnsVisibilitySkipped = ref();
const createForm = ref();

const tableModes = [
    {
        icon: 'view_column',
        title: t('table view'),
        value: TABLE_MODE,
        disable: $props.disableOption?.table,
    },
    {
        icon: 'grid_view',
        title: t('grid view'),
        value: CARD_MODE,
        disable: $props.disableOption?.card,
    },
];
onBeforeMount(() => {
    setUserParams(route.query[$props.searchUrl]);
    hasParams.value = params.value && Object.keys(params.value).length !== 0;
});

onMounted(() => {
    mode.value =
        quasar.platform.is.mobile && !$props.disableOption?.card
            ? CARD_MODE
            : $props.defaultMode;
    stateStore.rightDrawer = quasar.screen.gt.xs;
    columnsVisibilitySkipped.value = [
        ...splittedColumns.value.columns
            .filter((c) => c.visible == false)
            .map((c) => c.name),
        ...['tableActions'],
    ];
    createForm.value = $props.create;
    if ($props.create && route?.query?.createForm) {
        showForm.value = true;
        createForm.value = {
            ...createForm.value,
            ...{ formInitialData: JSON.parse(route?.query?.createForm) },
        };
    }
});

watch(
    () => $props.columns,
    (value) => splitColumns(value),
    { immediate: true }
);

watch(
    () => route.query[$props.searchUrl],
    (val) => setUserParams(val)
);

const isTableMode = computed(() => mode.value == TABLE_MODE);

function setUserParams(watchedParams, watchedOrder) {
    if (!watchedParams) return;

    if (typeof watchedParams == 'string') watchedParams = JSON.parse(watchedParams);
    const filter =
        typeof watchedParams?.filter == 'string'
            ? JSON.parse(watchedParams?.filter ?? '{}')
            : watchedParams?.filter;
    const where = filter?.where;
    const order = watchedOrder ?? filter?.order;

    watchedParams = { ...watchedParams, ...where };
    delete watchedParams.filter;
    delete params.value?.filter;
    params.value = { ...params.value, ...sanitizer(watchedParams) };
    orders.value = parseOrder(order);
}

function sanitizer(params) {
    for (const [key, value] of Object.entries(params)) {
        if (value && typeof value == 'object') {
            const param = Object.values(value)[0];
            if (typeof param == 'string') params[key] = param.replaceAll('%', '');
        }
    }
    return params;
}

function splitColumns(columns) {
    splittedColumns.value = {
        columns: [],
        chips: [],
        create: [],
        cardVisible: [],
    };

    for (const col of columns) {
        if (col.name == 'tableActions') {
            col.orderBy = false;
            splittedColumns.value.actions = col;
        }
        if (col.chip) splittedColumns.value.chips.push(col);
        if (col.isTitle) splittedColumns.value.title = col;
        if (col.create) splittedColumns.value.create.push(col);
        if (col.cardVisible) splittedColumns.value.cardVisible.push(col);
        if ($props.isEditable && col.disable == null) col.disable = false;
        if ($props.useModel && col.columnFilter != false)
            col.columnFilter = { ...col.columnFilter, inWhere: true };
        splittedColumns.value.columns.push(col);
    }
    // Status column
    if (splittedColumns.value.chips.length) {
        splittedColumns.value.columnChips = splittedColumns.value.chips.filter(
            (c) => !c.isId
        );
        if (splittedColumns.value.columnChips.length)
            splittedColumns.value.columns.unshift({
                align: 'left',
                label: t('status'),
                name: 'tableStatus',
                columnFilter: false,
                orderBy: false,
            });
    }
}

const rowClickFunction = computed(() => {
    if ($props.rowClick != undefined) return $props.rowClick;
    if ($props.redirect) return ({ id }) => redirectFn(id);
    return () => {};
});

const rowCtrlClickFunction = computed(() => {
    if ($props.rowCtrlClick != undefined) return $props.rowCtrlClick;
    if ($props.redirect)
        return (evt, { id }) => {
            stopEventPropagation(evt);
            window.open(`/#/${$props.redirect}/${id}`, '_blank');
        };
    return () => {};
});

function redirectFn(id) {
    router.push({ path: `/${$props.redirect}/${id}` });
}

function stopEventPropagation(event) {
    event.preventDefault();
    event.stopPropagation();
}

function reload(params) {
    selected.value = [];
    CrudModelRef.value.reload(params);
}

function columnName(col) {
    const column = { ...col, ...col.columnFilter };
    let name = column.name;
    if (column.alias) name = column.alias + '.' + name;
    return name;
}

function getColAlign(col) {
    return 'text-' + (col.align ?? 'left');
}

function parseOrder(urlOrders) {
    const orderObject = {};
    if (!urlOrders) return orderObject;
    if (typeof urlOrders == 'string') urlOrders = [urlOrders];
    for (const [index, orders] of urlOrders.entries()) {
        const [name, direction] = orders.split(' ');
        orderObject[name] = { direction, index: index + 1 };
    }
    return orderObject;
}

const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
defineExpose({
    create: createForm,
    reload,
    redirect: redirectFn,
    selected,
    CrudModelRef,
    params,
});

function handleOnDataSaved(_) {
    if (_.onDataSaved) _.onDataSaved(this);
    else $props.create.onDataSaved(_);
}
</script>
<template>
    <QDrawer
        v-if="$props.rightSearch"
        v-model="stateStore.rightDrawer"
        side="right"
        :width="256"
        show-if-above
    >
        <QScrollArea class="fit">
            <VnFilterPanel
                :data-key="$attrs['data-key']"
                :search-button="true"
                v-model="params"
                :search-url="searchUrl"
                :redirect="!!redirect"
                @set-user-params="setUserParams"
            >
                <template #body>
                    <div
                        class="row no-wrap flex-center"
                        v-for="col of splittedColumns.columns.filter(
                            (c) => c.columnFilter ?? true
                        )"
                        :key="col.id"
                    >
                        <VnTableFilter
                            :column="col"
                            :data-key="$attrs['data-key']"
                            v-model="params[columnName(col)]"
                            :search-url="searchUrl"
                        />
                        <VnTableOrder
                            v-if="
                                col?.columnFilter !== false &&
                                col?.name !== 'tableActions'
                            "
                            v-model="orders[col.orderBy ?? col.name]"
                            :name="col.orderBy ?? col.name"
                            :data-key="$attrs['data-key']"
                            :search-url="searchUrl"
                            :vertical="true"
                        />
                    </div>
                    <slot
                        name="moreFilterPanel"
                        :params="params"
                        :columns="splittedColumns.columns"
                    />
                </template>
            </VnFilterPanel>
        </QScrollArea>
    </QDrawer>
    <!-- class in div to fix warn-->
    <CrudModel
        v-bind="$attrs"
        :class="$attrs['class'] ?? 'q-px-md'"
        :limit="$attrs['limit'] ?? 20"
        ref="CrudModelRef"
        @on-fetch="(...args) => emit('onFetch', ...args)"
        :search-url="searchUrl"
        :disable-infinite-scroll="isTableMode"
        @save-changes="reload"
        :has-sub-toolbar="$props.hasSubToolbar ?? isEditable"
        :auto-load="hasParams || $attrs['auto-load']"
    >
        <template v-for="(_, slotName) in $slots" #[slotName]="slotData" :key="slotName">
            <slot :name="slotName" v-bind="slotData ?? {}" :key="slotName" />
        </template>
        <template #body="{ rows }">
            <QTable
                v-bind="table"
                class="vnTable"
                :columns="splittedColumns.columns"
                :rows="rows"
                v-model:selected="selected"
                :grid="!isTableMode"
                table-header-class="bg-header"
                card-container-class="grid-three"
                flat
                :style="isTableMode && `max-height: ${tableHeight}`"
                :virtual-scroll="isTableMode"
                @virtual-scroll="
                    (event) =>
                        event.index > rows.length - 2 &&
                        ($props.crudModel?.paginate ?? true) &&
                        CrudModelRef.vnPaginateRef.paginate()
                "
                @row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
                @update:selected="emit('update:selected', $event)"
            >
                <template #top-left v-if="!$props.withoutHeader">
                    <slot name="top-left"></slot>
                </template>
                <template #top-right v-if="!$props.withoutHeader">
                    <VnVisibleColumn
                        v-if="isTableMode"
                        v-model="splittedColumns.columns"
                        :table-code="tableCode ?? route.name"
                        :skip="columnsVisibilitySkipped"
                    />
                    <QBtnToggle
                        v-model="mode"
                        toggle-color="primary"
                        class="bg-vn-section-color"
                        dense
                        :options="tableModes.filter((mode) => !mode.disable)"
                    />
                    <QBtn
                        v-if="$props.rightSearch"
                        icon="filter_alt"
                        class="bg-vn-section-color q-ml-sm"
                        dense
                        @click="stateStore.toggleRightDrawer()"
                    />
                </template>
                <template #header-cell="{ col }">
                    <QTh v-if="col.visible ?? true">
                        <div
                            class="column self-start q-ml-xs ellipsis"
                            :class="`text-${col?.align ?? 'left'}`"
                            :style="$props.columnSearch ? 'height: 75px' : ''"
                        >
                            <div class="row items-center no-wrap" style="height: 30px">
                                <QTooltip v-if="col.toolTip">{{ col.toolTip }}</QTooltip>
                                <VnTableOrder
                                    v-model="orders[col.orderBy ?? col.name]"
                                    :name="col.orderBy ?? col.name"
                                    :label="col?.label"
                                    :data-key="$attrs['data-key']"
                                    :search-url="searchUrl"
                                />
                            </div>
                            <VnTableFilter
                                v-if="$props.columnSearch"
                                :column="col"
                                :show-title="true"
                                :data-key="$attrs['data-key']"
                                v-model="params[columnName(col)]"
                                :search-url="searchUrl"
                                class="full-width"
                            />
                        </div>
                    </QTh>
                </template>
                <template #header-cell-tableActions>
                    <QTh auto-width class="sticky" />
                </template>
                <template #body-cell-tableStatus="{ col, row }">
                    <QTd auto-width :class="getColAlign(col)">
                        <VnTableChip :columns="splittedColumns.columnChips" :row="row">
                            <template #afterChip>
                                <slot name="afterChip" :row="row"></slot>
                            </template>
                        </VnTableChip>
                    </QTd>
                </template>
                <template #body-cell="{ col, row, rowIndex }">
                    <!-- Columns -->
                    <QTd
                        auto-width
                        class="no-margin q-px-xs"
                        :class="[getColAlign(col), col.columnClass]"
                        v-if="col.visible ?? true"
                        @click.ctrl="
                            ($event) =>
                                rowCtrlClickFunction && rowCtrlClickFunction($event, row)
                        "
                    >
                        <slot
                            :name="`column-${col.name}`"
                            :col="col"
                            :row="row"
                            :row-index="rowIndex"
                        >
                            <VnTableColumn
                                :column="col"
                                :row="row"
                                :is-editable="col.isEditable ?? isEditable"
                                v-model="row[col.name]"
                                component-prop="columnField"
                            />
                        </slot>
                    </QTd>
                </template>
                <template #body-cell-tableActions="{ col, row }">
                    <QTd
                        auto-width
                        :class="getColAlign(col)"
                        class="sticky no-padding"
                        @click="stopEventPropagation($event)"
                    >
                        <QBtn
                            v-for="(btn, index) of col.actions"
                            v-show="btn.show ? btn.show(row) : true"
                            :key="index"
                            :title="btn.title"
                            :icon="btn.icon"
                            class="q-pa-xs"
                            flat
                            dense
                            :class="
                                btn.isPrimary ? 'text-primary-light' : 'color-vn-text '
                            "
                            :style="`visibility: ${
                                (btn.show && btn.show(row)) ?? true ? 'visible' : 'hidden'
                            }`"
                            @click="btn.action(row)"
                        />
                    </QTd>
                </template>
                <template #item="{ row, colsMap }">
                    <component
                        :is="$props.redirect ? 'router-link' : 'span'"
                        :to="`/${$props.redirect}/` + row.id"
                    >
                        <QCard
                            bordered
                            flat
                            class="row no-wrap justify-between cursor-pointer"
                            @click="
                                (_, row) => {
                                    $props.rowClick && $props.rowClick(row);
                                }
                            "
                        >
                            <QCardSection
                                vertical
                                class="no-margin no-padding"
                                :class="colsMap.tableActions ? 'w-80' : 'fit'"
                            >
                                <!-- Chips -->
                                <QCardSection
                                    v-if="splittedColumns.chips.length"
                                    class="no-margin q-px-xs q-py-none"
                                >
                                    <VnTableChip
                                        :columns="splittedColumns.chips"
                                        :row="row"
                                    >
                                        <template #afterChip>
                                            <slot name="afterChip" :row="row"></slot>
                                        </template>
                                    </VnTableChip>
                                </QCardSection>
                                <!-- Title -->
                                <QCardSection
                                    v-if="splittedColumns.title"
                                    class="q-pl-sm q-py-none text-primary-light text-bold text-h6 cardEllipsis"
                                >
                                    <span
                                        :title="row[splittedColumns.title.name]"
                                        @click="stopEventPropagation($event)"
                                        class="cursor-text"
                                    >
                                        {{ row[splittedColumns.title.name] }}
                                    </span>
                                </QCardSection>
                                <!-- Fields -->
                                <QCardSection
                                    class="q-pl-sm q-pr-lg q-py-xs"
                                    :class="$props.cardClass"
                                >
                                    <div
                                        v-for="(
                                            col, index
                                        ) of splittedColumns.cardVisible"
                                        :key="col.name"
                                        class="fields"
                                    >
                                        <VnLv
                                            :label="
                                                !col.component && col.label
                                                    ? `${col.label}:`
                                                    : ''
                                            "
                                        >
                                            <template #value>
                                                <span
                                                    @click="stopEventPropagation($event)"
                                                >
                                                    <slot
                                                        :name="`column-${col.name}`"
                                                        :col="col"
                                                        :row="row"
                                                        :row-index="index"
                                                    >
                                                        <VnTableColumn
                                                            :column="col"
                                                            :row="row"
                                                            :is-editable="false"
                                                            v-model="row[col.name]"
                                                            component-prop="columnField"
                                                            :show-label="true"
                                                        />
                                                    </slot>
                                                </span>
                                            </template>
                                        </VnLv>
                                    </div>
                                </QCardSection>
                            </QCardSection>
                            <!-- Actions -->
                            <QCardSection
                                v-if="colsMap.tableActions"
                                class="column flex-center w-10 no-margin q-pa-xs q-gutter-y-xs"
                                @click="stopEventPropagation($event)"
                            >
                                <QBtn
                                    v-for="(btn, index) of splittedColumns.actions
                                        .actions"
                                    :key="index"
                                    :title="btn.title"
                                    :icon="btn.icon"
                                    class="q-pa-xs"
                                    flat
                                    :class="
                                        btn.isPrimary
                                            ? 'text-primary-light'
                                            : 'color-vn-text '
                                    "
                                    @click="btn.action(row)"
                                />
                            </QCardSection>
                        </QCard>
                    </component>
                </template>
                <template #bottom-row="{ cols }" v-if="footer">
                    <QTr v-if="rows.length" class="bg-header" style="height: 30px">
                        <QTh
                            v-for="col of cols.filter((cols) => cols.visible ?? true)"
                            :key="col?.id"
                            class="text-center"
                        >
                            <slot
                                :name="`column-footer-${col.name}`"
                                :class="getColAlign(col)"
                            />
                        </QTh>
                    </QTr>
                </template>
            </QTable>
        </template>
    </CrudModel>
    <QPageSticky v-if="$props.create" :offset="[20, 20]" style="z-index: 2">
        <QBtn
            @click="
                () =>
                    createAsDialog ? (showForm = !showForm) : handleOnDataSaved(create)
            "
            color="primary"
            fab
            icon="add"
            shortcut="+"
        />
        <QTooltip>
            {{ createForm?.title }}
        </QTooltip>
    </QPageSticky>
    <QDialog v-model="showForm" transition-show="scale" transition-hide="scale">
        <FormModelPopup
            v-bind="createForm"
            :model="$attrs['data-key'] + 'Create'"
            @on-data-saved="(_, res) => createForm.onDataSaved(res)"
        >
            <template #form-inputs="{ data }">
                <div class="grid-create">
                    <slot
                        v-for="column of splittedColumns.create"
                        :key="column.name"
                        :name="`column-create-${column.name}`"
                        :data="data"
                        :column-name="column.name"
                        :label="column.label"
                    >
                        <VnTableColumn
                            :column="column"
                            :row="{}"
                            default="input"
                            v-model="data[column.name]"
                            :show-label="true"
                            component-prop="columnCreate"
                        />
                    </slot>
                    <slot name="more-create-dialog" :data="data" />
                </div>
            </template>
        </FormModelPopup>
    </QDialog>
</template>
<i18n>
en:
    status: Status
    table view: Table view
    grid view: Grid view
es:
    status: Estados
    table view: Vista en tabla
    grid view: Vista en cuadrĂ­cula
</i18n>

<style lang="scss">
.bg-chip-secondary {
    background-color: var(--vn-page-color);
    color: var(--vn-text-color);
}

.bg-header {
    background-color: var(--vn-accent-color);
    color: var(--vn-text-color);
}

.color-vn-text {
    color: var(--vn-text-color);
}

.q-table--dark .q-table__bottom,
.q-table--dark thead,
.q-table--dark tr {
    border-color: var(--vn-section-color);
}

.grid-three {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(400px, max-content));
    max-width: 100%;
    grid-gap: 20px;
    margin: 0 auto;
}

.grid-create {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, max-content));
    max-width: 100%;
    grid-gap: 20px;
    margin: 0 auto;
}

.flex-one {
    display: flex;
    flex-flow: row wrap;
    div.fields {
        width: 100%;
        .vn-label-value {
            display: flex;
            gap: 2%;
        }
    }
}

.q-table {
    th {
        padding: 0;
    }

    &__top {
        padding: 12px 0px;
        top: 0;
    }
}
.vnTable {
    thead tr th {
        position: sticky;
        z-index: 2;
    }
    thead tr:first-child th {
        top: 0;
    }
    .q-table__top {
        top: 0;
        padding: 12px 0;
    }
    tbody {
        .q-checkbox {
            display: flex;
            margin-bottom: 9px;
            & .q-checkbox__label {
                margin-left: 31px;
                color: var(--vn-text-color);
            }
            & .q-checkbox__inner {
                position: absolute;
                left: 0;
                color: var(--vn-label-color);
            }
        }
    }
    .sticky {
        position: sticky;
        right: 0;
    }
    td.sticky {
        background-color: var(--vn-section-color);
        z-index: 1;
    }
}

.vn-label-value {
    display: flex;
    flex-direction: row;
    color: var(--vn-text-color);
    .value {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        pointer-events: all;
        cursor: text;
        user-select: all;
    }
}

.cardEllipsis {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.grid-two {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, max-content));
    max-width: 100%;
    margin: 0 auto;
    overflow: scroll;
    white-space: wrap;
    width: 100%;
}

.w-80 {
    width: 80%;
}

.w-20 {
    width: 20%;
}

.cursor-text {
    pointer-events: all;
    cursor: text;
    user-select: all;
}
</style>