<script setup>
import axios from 'axios';
import { onMounted, onUnmounted, computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar';
import { useState } from 'src/composables/useState';
import { useStateStore } from 'stores/useStateStore';
import { useValidator } from 'src/composables/useValidator';
import useNotify from 'src/composables/useNotify.js';
import SkeletonForm from 'components/ui/SkeletonForm.vue';

const quasar = useQuasar();
const state = useState();
const stateStore = useStateStore();
const { t } = useI18n();
const { validate } = useValidator();
const { notify } = useNotify();

const $props = defineProps({
    url: {
        type: String,
        default: '',
    },
    model: {
        type: String,
        default: '',
    },
    filter: {
        type: Object,
        default: null,
    },
    urlUpdate: {
        type: String,
        default: null,
    },
    urlCreate: {
        type: String,
        default: null,
    },
    defaultActions: {
        type: Boolean,
        default: true,
    },
    autoLoad: {
        type: Boolean,
        default: false,
    },
    formInitialData: {
        type: Object,
        default: () => {},
    },
    observeFormChanges: {
        type: Boolean,
        default: true,
        description:
            'Esto se usa principalmente para permitir guardar sin hacer cambios (Útil para la feature de clonar ya que en este caso queremos poder guardar de primeras)',
    },
});

const emit = defineEmits(['onFetch']);

defineExpose({
    save,
});

onMounted(async () => {
    // Podemos enviarle al form la estructura de data inicial sin necesidad de fetchearla
    if ($props.formInitialData && !$props.autoLoad) {
        state.set($props.model, $props.formInitialData);
    } else {
        await fetch();
    }

    // Disparamos el watcher del form después de que se haya cargado la data inicial, si así se desea
    if ($props.observeFormChanges) {
        startFormWatcher();
    }
});

onUnmounted(() => {
    state.unset($props.model);
});

const isLoading = ref(false);
// Si elegimos observar los cambios del form significa que inicialmente las actions estaran deshabilitadas
const hasChanges = ref(!$props.observeFormChanges);
const originalData = ref({ ...$props.formInitialData });
const formData = computed(() => state.get($props.model));
const formUrl = computed(() => $props.url);

const startFormWatcher = () => {
    watch(
        () => formData.value,
        (val) => {
            if (val) hasChanges.value = true;
        },
        { deep: true }
    );
};

function tMobile(...args) {
    if (!quasar.platform.is.mobile) return t(...args);
}

async function fetch() {
    const { data } = await axios.get($props.url, {
        params: { filter: JSON.stringify($props.filter) },
    });

    state.set($props.model, data);
    originalData.value = data && JSON.parse(JSON.stringify(data));

    emit('onFetch', state.get($props.model));
}

async function save() {
    if (!hasChanges.value) {
        notify('globals.noChanges', 'negative');
        return;
    }
    isLoading.value = true;

    try {
        if ($props.urlCreate) {
            await axios.post($props.urlCreate, formData.value);
            notify('globals.dataCreated', 'positive');
        } else {
            await axios.patch($props.urlUpdate || $props.url, formData.value);
        }
    } catch (err) {
        notify('errors.create', 'negative');
    }

    originalData.value = JSON.parse(JSON.stringify(formData.value));
    hasChanges.value = false;
    isLoading.value = false;
}

function reset() {
    state.set($props.model, originalData.value);
    originalData.value = JSON.parse(JSON.stringify(originalData.value));

    emit('onFetch', state.get($props.model));
    if ($props.observeFormChanges) {
        hasChanges.value = false;
    }
}

// eslint-disable-next-line vue/no-dupe-keys
function filter(value, update, filterOptions) {
    update(
        () => {
            const { options, filterFn } = filterOptions;

            options.value = filterFn(options, value);
        },
        (ref) => {
            ref.setOptionIndex(-1);
            ref.moveOptionSelection(1, true);
        }
    );
}

watch(formUrl, async () => {
    originalData.value = null;
    reset();
    fetch();
});
</script>
<template>
    <QBanner v-if="$props.observeFormChanges && hasChanges" class="text-white bg-warning">
        <QIcon name="warning" size="md" class="q-mr-md" />
        <span>{{ t('globals.changesToSave') }}</span>
    </QBanner>
    <div class="column items-center">
        <QForm
            v-if="formData"
            @submit="save"
            @reset="reset"
            class="q-pa-md"
            id="formModel"
        >
            <QCard>
                <slot
                    name="form"
                    :data="formData"
                    :validate="validate"
                    :filter="filter"
                />
            </QCard>
        </QForm>
    </div>
    <Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
        <div v-if="$props.defaultActions">
            <QBtnGroup push class="q-gutter-x-sm">
                <slot name="moreActions" />
                <QBtn
                    :label="tMobile('globals.reset')"
                    color="primary"
                    icon="restart_alt"
                    flat
                    @click="reset"
                    :disable="!hasChanges"
                    :title="t('globals.reset')"
                />
                <QBtn
                    :label="tMobile('globals.save')"
                    color="primary"
                    icon="save"
                    @click="save"
                    :disable="!hasChanges"
                    :title="t('globals.save')"
                />
            </QBtnGroup>
        </div>
    </Teleport>
    <SkeletonForm v-if="!formData" />
    <QInnerLoading
        :showing="isLoading"
        :label="t('globals.pleaseWait')"
        color="primary"
    />
</template>
<style lang="scss" scoped>
#formModel {
    max-width: 800px;
    width: 100%;
}

.q-card {
    padding: 32px;
}
</style>
