<template>
  <q-table
    v-model:fullscreen="fullscreen"
    flat
    row-key="id"
    :pagination="{ rowsPerPage }"
    :pagination-label="getPaginationLabel"
    :rows-per-page-label="$t('common.ui.table.rows_per_page')"
    :grid="$q.screen.lt.sm"
    :visible-columns
    :filter
    :columns
    :loading
    :rows
    :hide-top="semTableTop"
    class="softpar-table q-pa-sm"
    @row-click="onRowClick"
  >
    <template v-if="!semTableTop" #top>
      <div
        v-if="title || showSearchField || showColumnsSelect || addItem || $slots.top"
        class="flex full-width align-center q-mb-md q-pl-sm"
        style="gap: 1rem"
      >
        <div v-if="title" class="column text-h6">
          <span>{{ title }}</span>
          <span class="text-body2">{{
            rows.length + " " + $t("common.ui.table.records_found")
          }}</span>
        </div>
        <DefaultInput
          v-if="showSearchField"
          v-model="filter"
          debounce="250"
          :placeholder="$t('common.actions.search')"
          clearable
          autofocus
          class="q-mt-sm q-ml-sm"
          color="primary"
        >
          <template #append>
            <q-icon name="mdi-magnify" />
          </template>
        </DefaultInput>
        <DefaultSelect
          v-if="showColumnsSelect"
          v-model="visibleColumns"
          multiple
          options-outlined
          :display-value="$q.lang.table.columns"
          emit-value
          map-options
          :options="mapColumns"
          style="width: 150px"
          options-selected-class="text-bold"
        />
        <slot name="top" :rows="rows" />
        <q-btn
          v-if="addItem"
          color="primary"
          padding="10px 16px"
          :outline="outlineAdd"
          :label="$t('common.actions.add')"
          @click="onAddItem"
        >
        </q-btn>
      </div>
    </template>

    <template #body-cell-actions="{ row }">
      <q-td auto-width>
        <q-item-section class="no-wrap" style="flex-direction: row">
          <slot name="body-cell-actions" :row="row" />
          <q-btn
            v-if="deleteFunction"
            outline
            icon="mdi-trash-can-outline"
            style="width: 36px"
            class="q-ml-auto q-mr-sm"
            @click.prevent.stop="onDelete(row.id)"
          />
        </q-item-section>
      </q-td>
    </template>

    <template #item="{ cols, row, index }">
      <div class="q-pa-xs col-xs-12 col-sm-6 col-md-4 col-lg-3">
        <q-card
          bordered
          flat
          class="q-pa-sm"
          @click="onRowClick($event, row, index)"
        >
          <q-list dense>
            <q-item
              v-for="col in cols.filter((col) => col.name !== 'desc')"
              :key="col.name"
            >
              <template v-if="col.name !== 'actions'">
                <q-item-section>
                  <q-item-label caption>{{ col.label }}</q-item-label>
                  <q-item-label>{{ col.value }}</q-item-label>
                </q-item-section>
              </template>
              <template v-else>
                <slot name="body-cell-actions" :row="row" />
                <q-item-section v-if="deleteFunction">
                  <q-btn
                    v-if="deleteFunction"
                    outline
                    icon="mdi-trash-can-outline"
                    style="width: 36px"
                    class="q-mr-sm"
                    @click.prevent.stop="onDelete(row.id)"
                  />
                </q-item-section>
              </template>
            </q-item>
          </q-list>
        </q-card>
      </div>
    </template>

    <template #loading>
      <q-inner-loading showing color="primary" />
    </template>

    <template v-if="!hideNoDataLabel" #no-data>
      <div v-if="!loading" class="q-my-md row justify-center full-width">
        <div class="q-pa-md body2">
          {{ $t("http.errors.no_records_found") }}
        </div>
      </div>
    </template>
    <template v-for="slotName in usableSlots($slots)" #[slotName]="data">
      <slot :name="slotName" v-bind="data" />
    </template>
  </q-table>
</template>

<script setup>
import { ref, onMounted, toRaw, watch } from "vue";
import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router";
import { useQuasar } from "quasar";

import DefaultInput from "./DefaultInput.vue";
import DefaultSelect from "./DefaultSelect.vue";

const emit = defineEmits(["onRowClick", "onAddItem", "noRows"]);

const {
  columns,
  apiCall,
  outlineAdd,
  openItem,
  openItemRoute,
  addItem,
  addItemRoute,
  rowsPerPage,
  showSearchField,
  noApiCall,
  hideNoDataLabel,
  deleteFunction,
  semTableTop,
} = defineProps({
  title: {
    type: String,
    default: null,
  },
  columns: {
    type: Array,
    required: true,
  },
  apiCall: {
    type: Function,
    default: null,
  },
  outlineAdd: {
    type: Boolean,
    default: false,
  },
  openItem: {
    type: Boolean,
    default: false,
  },
  openItemRoute: {
    type: String,
    default: "",
  },
  addItem: {
    type: Boolean,
    default: false,
  },
  addItemRoute: {
    type: String,
    default: "",
  },
  rowsPerPage: {
    type: Number,
    default: 10,
  },
  showSearchField: {
    type: Boolean,
    default: true,
  },
  showColumnsSelect: {
    type: Boolean,
    default: false,
  },
  noApiCall: {
    type: Boolean,
    default: false,
  },
  hideNoDataLabel: {
    type: Boolean,
    default: false,
  },
  deleteFunction: {
    type: Function,
    default: null,
  },
  semTableTop: {
    type: Boolean,
    default: true,
  },
});

const router = useRouter();
const { t } = useI18n();
const $q = useQuasar();

const rows = defineModel("rows", { type: Array, default: [] });
const filter = ref("");
const loading = ref(true);
const fullscreen = ref(false);

const mapColumns = columns.reduce((accm, column) => {
  if (!column.required) {
    accm.push({
      label: column.label,
      value: column.name,
    });
  }
  return accm;
}, []);

const visibleColumns = ref(mapColumns.map((column) => column.value));

const getPaginationLabel = (from, to, last) => {
  return `${from}-${to} ${t("common.ui.table.of")} ${last}`;
};

const onRowClick = (evt, row, index) => {
  const item = toRaw(row);
  if (openItem) {
    if (openItemRoute) {
      router.push({ name: openItemRoute, params: { id: item.id } });
    } else {
      emit("onRowClick", { evt, row, index });
    }
  }
};

const onAddItem = () => {
  if (addItem) {
    if (addItemRoute) {
      router.push({ name: addItemRoute });
    } else {
      emit("onAddItem");
    }
  }
};

const onDelete = async (id) => {
  if (deleteFunction) {
    $q.dialog({
      title: t("common.ui.messages.confirm_action"),
      message: t("common.ui.messages.are_you_sure_delete"),
      ok: {
        color: "negative",
        label: t("common.actions.delete"),
      },
      cancel: {
        color: "primary",
        outline: true,
        label: t("common.actions.cancel"),
      },
    }).onOk(async () => {
      loading.value = true;
      try {
        await deleteFunction(id);
        await onRequest();
      } catch (error) {
        console.error(error);
      } finally {
        loading.value = false;
      }
    });
  }
};

const onRequest = async () => {
  if (noApiCall) {
    loading.value = false;
    return;
  }
  loading.value = true;

  const response = await apiCall();
  rows.value = response;

  if (rows.value.length == 0) {
    emit("noRows");
  }
  loading.value = false;
};

const usableSlots = (slots) => {
  const availableSlots = Object.keys(slots);
  const usableSlots = availableSlots.filter(
    (slot) =>
      !["body-cell-actions", "top", "loading", "no-data"].includes(slot),
  );
  return usableSlots;
};

watch(
  () => apiCall,
  async () => {
    await onRequest();
  },
);

onMounted(async () => {
  await onRequest({
    filter: undefined,
  });
});

defineExpose({
  refresh: onRequest,
});
</script>

<style lang="scss">
@import "src/css/table.scss";
</style>
