<script setup>
import { ref, inject, onMounted, computed, Teleport } from 'vue';
import { useI18n } from 'vue-i18n';

import { useAppStore } from 'stores/app';
import { storeToRefs } from 'pinia';
import useNotify from 'src/composables/useNotify.js';
import {
    generateUpdateSqlQuery,
    generateInsertSqlQuery
} from 'src/js/db/sqlService.js';

const props = defineProps({
    title: {
        type: String,
        default: ''
    },
    table: {
        type: String,
        default: ''
    },
    schema: {
        type: String,
        default: ''
    },
    // Objeto que define las pks de la tabla. Usado para generar las queries sql correspondientes.
    // Debe ser definido como un objeto de pares key-value, donde la clave es el nombre de la columna de la pk.
    pks: {
        type: Object,
        default: () => {}
    },
    createModelDefault: {
        type: Object,
        default: () => ({
            field: '',
            value: ''
        })
    },
    // Objeto que contiene la consulta SQL y los parámetros necesarios para obtener los datos iniciales del formulario.
    // `query` debe ser una cadena de texto que representa la consulta SQL.
    // `params` es un objeto que mapea los parámetros de la consulta a sus valores.
    fetchFormDataSql: {
        type: Object,
        default: () => ({
            query: '',
            params: {}
        })
    },
    // Objeto con los datos iniciales del form, si este objeto es definido, no se ejecuta la query fetchFormDataSql
    formInitialData: {
        type: Object,
        default: () => {}
    },
    // Array de columnas que no se deben actualizar
    columnsToIgnoreUpdate: {
        type: Array,
        default: () => []
    },
    autoLoad: {
        type: Boolean,
        default: true
    },
    isEditMode: {
        type: Boolean,
        default: true
    },
    defaultActions: {
        type: Boolean,
        default: true
    },
    showBottomActions: {
        type: Boolean,
        default: false
    },
    saveFn: {
        type: Function,
        default: null
    },
    separationBetweenInputs: {
        type: String,
        default: 'xs'
    }
});

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

const { t } = useI18n();
const jApi = inject('jApi');
const { notify } = useNotify();
const appStore = useAppStore();
const { isHeaderMounted } = storeToRefs(appStore);

const loading = ref(false);
const formData = ref({});
const addressFormRef = ref(null);
const modelInfo = ref(null);
// Array de nombre de columnas de la tabla
const tableColumns = computed(
    () => modelInfo.value?.columns.map(col => col.name) || []
);
// Array de nombre de columnas que fueron actualizadas y no estan en columnsToIgnoreUpdate
const updatedColumns = computed(() => {
    return tableColumns.value.filter(
        colName =>
            modelInfo.value?.data[0][colName] !== formData.value[colName] &&
            !props.columnsToIgnoreUpdate.includes(colName)
    );
});

const hasChanges = computed(() => !!updatedColumns.value.length);

const separationBetweenInputs = computed(() => {
    return `q-gutter-y-${props.separationBetweenInputs}`;
});

const fetchFormData = async () => {
    if (!props.fetchFormDataSql.query) return;
    loading.value = true;
    const { results } = await jApi.execQuery(
        props.fetchFormDataSql.query,
        props.fetchFormDataSql.params
    );

    modelInfo.value = results[0];

    if (!modelInfo.value.data[0]) {
        modelInfo.value.data[0] = {};
        // Si no existen datos iniciales, se inicializan con null, en base a las columnas de la tabla
        modelInfo.value.columns.forEach(
            col => (modelInfo.value.data[0][col.name] = null)
        );
    }

    formData.value = { ...modelInfo.value.data[0] };
    loading.value = false;
};

const onSubmitSuccess = () => {
    emit('onDataSaved');
    notify(t('dataSaved'), 'positive');
};

const submit = async () => {
    try {
        if (props.saveFn) {
            await props.saveFn(formData.value);
        } else {
            if (!hasChanges.value) {
                return;
            }
            const sqlQuery = generateSqlQuery();
            await jApi.execQuery(sqlQuery, props.pks);
            modelInfo.value.data[0] = { ...formData.value };
        }
        onSubmitSuccess();
    } catch (error) {
        console.error('Error:', error);
    }
};

const generateSqlQuery = () => {
    if (props.isEditMode) {
        return generateUpdateSqlQuery(
            props.schema,
            props.table,
            props.pks,
            updatedColumns.value,
            formData.value
        );
    } else {
        return generateInsertSqlQuery(
            props.schema,
            props.table,
            formData.value,
            updatedColumns.value,
            props.createModelDefault
        );
    }
};

onMounted(async () => {
    if (!props.formInitialData && props.autoLoad) {
        fetchFormData();
    } else {
        formData.value = { ...props.formInitialData };
    }
});

defineExpose({
    formData,
    submit
});
</script>

<template>
    <QCard class="form-container" v-bind="$attrs">
        <QForm
            v-if="!loading"
            ref="addressFormRef"
            class="column full-width"
            :class="separationBetweenInputs"
            @submit="submit()"
        >
            <span v-if="title" class="text-h6 text-bold">
                {{ title }}
            </span>
            <slot name="form" :data="formData" />
            <component
                v-if="isHeaderMounted"
                :is="showBottomActions ? 'div' : Teleport"
                to="#actions"
                class="flex row justify-end q-gutter-x-sm"
                :class="{ 'q-mt-md': showBottomActions }"
            >
                <QBtn
                    v-if="defaultActions && showBottomActions"
                    :label="t('cancel')"
                    :icon="showBottomActions ? undefined : 'check'"
                    rounded
                    no-caps
                    flat
                    v-close-popup
                />
                <QBtn
                    v-if="defaultActions"
                    :label="t('save')"
                    type="submit"
                    :icon="showBottomActions ? undefined : 'check'"
                    rounded
                    no-caps
                    flat
                    :disabled="!showBottomActions && !updatedColumns.length"
                />
                <slot name="actions" :data="formData" />
            </component>
        </QForm>
        <QSpinner v-else color="primary" size="3em" :thickness="2" />
    </QCard>
</template>

<style lang="scss" scoped>
.form-container {
    width: 100%;
    height: max-content;
    max-width: 544px;
    padding: 32px;
    display: flex;
    justify-content: center;
}
</style>
