<template>
    <a
        class="btn"
        :class="getClassButton"
        :title="buttonTitle"
        @click="openModal"
    >
        <span v-if="displayTextButton">{{ buttonTitle }}</span>
    </a>

    <teleport to="body">
        <modal
            v-if="showModal"
            @close="closeModal"
            :modal-dialog-class="modalDialogClass"
            :show="showModal"
            :hide-footer="false"
        >
            <template #header>
                <h3 class="modal-title">
                    {{ modalTitle }}
                </h3>
            </template>

            <template #body-head>
                <div class="modal-body">
                    <div class="search">
                        <label class="col-form-label" style="float: right">
                            {{
                                trans(ADD_PERSONS_SUGGESTED_COUNTER, {
                                    count: suggestedCounter,
                                })
                            }}
                        </label>

                        <input
                            id="search-persons"
                            name="query"
                            v-model="query"
                            :placeholder="
                                trans(ADD_PERSONS_SEARCH_SOME_PERSONS)
                            "
                            ref="searchRef"
                        />
                        <i class="fa fa-search fa-lg" />
                        <i
                            class="fa fa-times"
                            v-if="queryLength >= 3"
                            @click="resetSuggestion"
                        />
                    </div>
                </div>
                <div class="modal-body" v-if="checkUniq === 'checkbox'">
                    <div class="count">
                        <span>
                            <a v-if="suggestedCounter > 2" @click="selectAll">
                                {{ trans(ACTION_CHECK_ALL) }}
                            </a>
                            <a
                                v-if="selectedCounter > 0"
                                @click="resetSelection"
                            >
                                <i v-if="suggestedCounter > 2"> • </i>
                                {{ trans(ACTION_RESET) }}
                            </a>
                        </span>
                        <span v-if="selectedCounter > 0">
                            {{
                                trans(ADD_PERSONS_SELECTED_COUNTER, {
                                    count: selectedCounter,
                                })
                            }}
                        </span>
                    </div>
                </div>
            </template>

            <template #body>
                <div class="results">
                    <person-suggestion
                        v-for="item in selectedAndSuggested.slice().reverse()"
                        :key="itemKey(item)"
                        :item="item"
                        :search="search"
                        :type="checkUniq"
                        @save-form-on-the-fly="saveFormOnTheFly"
                        @new-prior-suggestion="newPriorSuggestion"
                        @update-selected="updateSelected"
                    />

                    <div class="create-button">
                        <on-the-fly
                            v-if="
                                queryLength >= 3 &&
                                (options.type.includes('person') ||
                                    options.type.includes('thirdparty'))
                            "
                            :button-text="
                                trans(ONTHEFLY_CREATE_BUTTON, { q: query })
                            "
                            :allowed-types="options.type"
                            :query="query"
                            action="create"
                            @save-form-on-the-fly="saveFormOnTheFly"
                            ref="onTheFly"
                        />
                    </div>
                </div>
            </template>

            <template #footer>
                <button
                    class="btn btn-create"
                    @click.prevent="
                        () => {
                            $emit('addNewPersons', {
                                selected: selectedComputed,
                            });
                            query = '';
                            closeModal();
                        }
                    "
                >
                    {{ trans(ACTION_ADD) }}
                </button>
            </template>
        </modal>
    </teleport>
</template>

<script setup lang="ts">
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import { ref, reactive, computed, nextTick, watch, shallowRef } from "vue";
import PersonSuggestion from "./AddPersons/PersonSuggestion.vue";
import { searchEntities } from "ChillPersonAssets/vuejs/_api/AddPersons";
import OnTheFly from "ChillMainAssets/vuejs/OnTheFly/components/OnTheFly.vue";

import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";

import {
    trans,
    ADD_PERSONS_SUGGESTED_COUNTER,
    ADD_PERSONS_SEARCH_SOME_PERSONS,
    ADD_PERSONS_SELECTED_COUNTER,
    ONTHEFLY_CREATE_BUTTON,
    ACTION_CHECK_ALL,
    ACTION_RESET,
    ACTION_ADD,
} from "translator";
import {
    Suggestion,
    Search,
    Result as OriginalResult,
    SearchOptions,
} from "ChillPersonAssets/types";

// Extend Result type to include optional addressId
type Result = OriginalResult & { addressId?: number };

const props = defineProps({
    suggested: { type: Array as () => Suggestion[], default: () => [] },
    selected: { type: Array as () => Suggestion[], default: () => [] },
    buttonTitle: { type: String, required: true },
    modalTitle: { type: String, required: true },
    options: { type: Object as () => SearchOptions, required: true },
});

defineEmits(["addNewPersons"]);

const showModal = ref(false);
const modalDialogClass = ref("modal-dialog-scrollable modal-xl");

const modal = shallowRef({
    showModal: false,
    modalDialogClass: "modal-dialog-scrollable modal-xl",
});

const search = reactive({
    query: "" as string,
    previousQuery: "" as string,
    currentSearchQueryController: null as AbortController | null,
    suggested: props.suggested as Suggestion[],
    selected: props.selected as Suggestion[],
    priorSuggestion: {} as Partial<Suggestion>,
});

const searchRef = ref<HTMLInputElement | null>(null);
const onTheFly = ref<InstanceType<typeof OnTheFly> | null>(null);

const query = computed({
    get: () => search.query,
    set: (val) => setQuery(val),
});
const queryLength = computed(() => search.query.length);
const suggestedCounter = computed(() => search.suggested.length);
const selectedComputed = computed(() => search.selected);
const selectedCounter = computed(() => search.selected.length);

const getClassButton = computed(() => {
    let size = props.options?.button?.size ?? "";
    let type = props.options?.button?.type ?? "btn-create";
    return size ? size + " " + type : type;
});
const displayTextButton = computed(() =>
    props.options?.button?.display !== undefined
        ? props.options.button.display
        : true,
);

const checkUniq = computed(() =>
    props.options.uniq === true ? "radio" : "checkbox",
);

const priorSuggestion = computed(() => search.priorSuggestion);
const hasPriorSuggestion = computed(() => !!search.priorSuggestion.key);

const itemKey = (item: Suggestion) => item.result.type + item.result.id;

function addPriorSuggestion() {
    if (hasPriorSuggestion.value) {
        // Type assertion is safe here due to the checks above
        search.suggested.unshift(priorSuggestion.value as Suggestion);
        search.selected.unshift(priorSuggestion.value as Suggestion);
        newPriorSuggestion(null);
    }
}

const selectedAndSuggested = computed(() => {
    addPriorSuggestion();
    const uniqBy = (a: Suggestion[], key: (item: Suggestion) => string) => [
        ...new Map(a.map((x) => [key(x), x])).values(),
    ];
    let union = [
        ...new Set([
            ...search.suggested.slice().reverse(),
            ...search.selected.slice().reverse(),
        ]),
    ];
    return uniqBy(union, (k: Suggestion) => k.key);
});

function openModal() {
    showModal.value = true;
    nextTick(() => {
        if (searchRef.value) searchRef.value.focus();
    });
}
function closeModal() {
    showModal.value = false;
}

function setQuery(q: string) {
    search.query = q;

    // Clear previous search if any
    if (search.currentSearchQueryController) {
        search.currentSearchQueryController.abort();
        search.currentSearchQueryController = null;
    }

    if (q === "") {
        loadSuggestions([]);
        return;
    }

    // Debounce delay based on query length
    const delay = q.length > 3 ? 300 : 700;

    setTimeout(() => {
        // Only search if query hasn't changed in the meantime
        if (q !== search.query) return;

        search.currentSearchQueryController = new AbortController();

        searchEntities(
            { query: q, options: props.options },
            search.currentSearchQueryController.signal,
        )
            .then((suggested: Search) => {
                loadSuggestions(suggested.results);
            })
            .catch((error: DOMException) => {
                if (
                    error instanceof DOMException &&
                    error.name === "AbortError"
                ) {
                    // Request was aborted, ignore
                    return;
                }
                throw error;
            });
    }, delay);
}

function loadSuggestions(suggestedArr: Suggestion[]) {
    search.suggested = suggestedArr;
    search.suggested.forEach((item) => {
        item.key = itemKey(item);
    });
}

function updateSelected(value: Suggestion[]) {
    search.selected = value;
}

function resetSuggestion() {
    search.query = "";
    search.suggested = [];
}

function resetSelection() {
    search.selected = [];
}

function resetSearch() {
    resetSelection();
    resetSuggestion();
}

function selectAll() {
    search.suggested.forEach((item) => {
        search.selected.push(item);
    });
}

function newPriorSuggestion(entity: Result | null) {
    if (entity !== null) {
        let suggestion = {
            key: entity.type + entity.id,
            relevance: 0.5,
            result: entity,
        };
        search.priorSuggestion = suggestion;
    } else {
        search.priorSuggestion = {};
    }
}

async function saveFormOnTheFly({
    type,
    data,
}: {
    type: string;
    data: Result;
}) {
    try {
        if (type === "person") {
            const responsePerson: Result = await makeFetch(
                "POST",
                "/api/1.0/person/person.json",
                data,
            );
            newPriorSuggestion(responsePerson);
            if (onTheFly.value) onTheFly.value.closeModal();

            if (data.addressId != null) {
                const household = { type: "household" };
                const address = { id: data.addressId };
                try {
                    const responseHousehold: Result = await makeFetch(
                        "POST",
                        "/api/1.0/person/household.json",
                        household,
                    );
                    const member = {
                        concerned: [
                            {
                                person: {
                                    type: "person",
                                    id: responsePerson.id,
                                },
                                start_date: {
                                    datetime: `${new Date().toISOString().split("T")[0]}T00:00:00+02:00`,
                                },
                                holder: false,
                                comment: null,
                            },
                        ],
                        destination: {
                            type: "household",
                            id: responseHousehold.id,
                        },
                        composition: null,
                    };
                    await makeFetch(
                        "POST",
                        "/api/1.0/person/household/members/move.json",
                        member,
                    );
                    try {
                        const _response = await makeFetch(
                            "POST",
                            `/api/1.0/person/household/${responseHousehold.id}/address.json`,
                            address,
                        );
                        console.log(_response);
                    } catch (error) {
                        console.error(error);
                    }
                } catch (error) {
                    console.error(error);
                }
            }
        } else if (type === "thirdparty") {
            const response: Result = await makeFetch(
                "POST",
                "/api/1.0/thirdparty/thirdparty.json",
                data,
            );
            newPriorSuggestion(response);
            if (onTheFly.value) onTheFly.value.closeModal();
        }
    } catch (error) {
        console.error(error);
    }
}

watch(
    () => props.selected,
    (newSelected) => {
        search.selected = newSelected;
    },
    { deep: true },
);

watch(
    () => props.suggested,
    (newSuggested) => {
        search.suggested = newSuggested;
    },
    { deep: true },
);

watch(
    () => modal,
    (val) => {
        showModal.value = val.value.showModal;
        modalDialogClass.value = val.value.modalDialogClass;
    },
    { deep: true },
);

defineExpose({
    resetSearch,
    showModal,
});
</script>

<style lang="scss">
li.add-persons {
    a {
        cursor: pointer;
    }
}
div.body-head {
    overflow-y: unset;
    div.modal-body:first-child {
        margin: auto 4em;
        div.search {
            position: relative;
            input {
                width: 100%;
                padding: 1.2em 1.5em 1.2em 2.5em;
                //margin: 1em 0;
            }
            i {
                position: absolute;
                opacity: 0.5;
                padding: 0.65em 0;
                top: 50%;
            }
            i.fa-search {
                left: 0.5em;
            }
            i.fa-times {
                right: 1em;
                padding: 0.75em 0;
                cursor: pointer;
            }
        }
    }
    div.modal-body:last-child {
        padding-bottom: 0;
    }
    div.count {
        margin: -0.5em 0 0.7em;
        display: flex;
        justify-content: space-between;
        a {
            cursor: pointer;
        }
    }
}
.create-button > a {
    margin-top: 0.5em;
    margin-left: 2.6em;
}
</style>
