<template>
    <div class="form-item-stack">
        <el-switch
            :model-value="modelValue.applyToAll"
            :validate-event="false"
            active-text="All products"
            @update:model-value="value => updateScope({ applyToAll: Boolean(value) })"
        />

        <template v-if="!modelValue.applyToAll">
            <div class="scope-field">
                <span class="secondary-text m-0">Applies to all products in category</span>
                <el-select
                    :model-value="modelValue.categoryIds"
                    :validate-event="false"
                    multiple
                    collapse-tags
                    :max-collapse-tags="3"
                    collapse-tags-tooltip
                    class="cms-scope-select"
                    placeholder="Select categories"
                    @update:model-value="value => updateScope({ categoryIds: value })"
                >
                    <el-option
                        v-for="category in categories"
                        :key="category.id"
                        :label="category.name"
                        :value="category.id"
                    />
                </el-select>
            </div>

            <div class="scope-field">
                <span class="secondary-text m-0">Applies to all variants of the product</span>
                <el-select
                    :model-value="modelValue.productIds"
                    :validate-event="false"
                    multiple
                    filterable
                    remote
                    reserve-keyword
                    collapse-tags
                    :max-collapse-tags="3"
                    collapse-tags-tooltip
                    class="cms-scope-select"
                    :remote-method="loadProducts"
                    :loading="productSearchLoading"
                    placeholder="Search products"
                    @update:model-value="value => updateScope({ productIds: value })"
                >
                    <el-option
                        v-for="product in productOptions"
                        :key="product.id"
                        :label="product.title"
                        :value="product.id"
                    />
                </el-select>
            </div>

            <div class="scope-field">
                <span class="secondary-text m-0">Applies to selected variants of the product</span>
                <el-select
                    :model-value="modelValue.variantIds"
                    :validate-event="false"
                    multiple
                    filterable
                    remote
                    reserve-keyword
                    collapse-tags
                    :max-collapse-tags="3"
                    collapse-tags-tooltip
                    class="cms-scope-select"
                    :remote-method="loadVariants"
                    :loading="variantSearchLoading"
                    placeholder="Search variants"
                    @update:model-value="value => updateScope({ variantIds: value })"
                >
                    <el-option
                        v-for="variant in variantOptions"
                        :key="variant.id"
                        :label="variant.label"
                        :value="variant.id"
                    />
                </el-select>
            </div>
        </template>
    </div>
</template>

<script setup lang="ts">
import { getProductTitle } from '@/utils/product/getProductTitle';
import { getVariantLabel } from '@/utils/product/getVariantLabel';
import { useCategoriesStore } from '@/stores/categories';
import { useProductsStore } from '@/stores/products';
import type { DiscountCode } from '@/types/discountCode/DiscountCode';
import type { DiscountScope } from '@/types/discountCode/DiscountScope';
import type { Product } from '@/types/product/Product';
import type { ProductOption } from '@/types/product/ProductOption';
import type { ProductVariant } from '@/types/product/ProductVariant';
import type { VariantOption } from '@/types/product/VariantOption';
import { ElMessage } from 'element-plus';
import { onMounted, ref, type PropType } from 'vue';
import { storeToRefs } from 'pinia';

const props = defineProps({
    modelValue: {
        type: Object as PropType<DiscountScope>,
        required: true
    },
    discountCode: {
        type: Object as PropType<DiscountCode | null>,
        default: null
    }
});

const emit = defineEmits<{
    'update:modelValue': [value: DiscountScope];
    change: [];
}>();

const { fetchProducts, fetchProductVariants } = useProductsStore();
const { categories } = storeToRefs(useCategoriesStore());

const productOptions = ref<ProductOption[]>([]);
const variantOptions = ref<VariantOption[]>([]);
const productSearchLoading = ref(false);
const variantSearchLoading = ref(false);

onMounted(() => {
    if (props.discountCode) {
        mergeProductOptions(props.discountCode.products?.map(productOptionFromEntity) ?? []);
        mergeVariantOptions(props.discountCode.variants?.map(variantOptionFromEntity) ?? []);
    }
});

const mergeProductOptions = (products: ProductOption[]): void => {
    const selectedIds = new Set(props.modelValue.productIds);

    const byId = new Map(
        productOptions.value.filter(product => selectedIds.has(product.id)).map(product => [product.id, product])
    );

    for (const product of products) {
        byId.set(product.id, product);
    }

    productOptions.value = [...byId.values()].sort((a, b) => a.title.localeCompare(b.title));
};

const productOptionFromEntity = (product: Pick<Product, 'id' | 'title'>): ProductOption => ({
    id: product.id,
    title: getProductTitle(product.title)
});

const mergeVariantOptions = (variants: VariantOption[]): void => {
    const selectedIds = new Set(props.modelValue.variantIds);

    const byId = new Map(
        variantOptions.value.filter(variant => selectedIds.has(variant.id)).map(variant => [variant.id, variant])
    );

    for (const variant of variants) {
        byId.set(variant.id, variant);
    }

    variantOptions.value = [...byId.values()].sort((a, b) => a.label.localeCompare(b.label));
};

const variantOptionFromEntity = (variant: ProductVariant): VariantOption => ({
    id: variant.id,
    label: getVariantLabel(variant.title, variant.product?.title)
});

const loadProducts = async (search: string) => {
    productSearchLoading.value = true;

    try {
        const { items } = await fetchProducts({ search, page: 1, limit: 20 });

        mergeProductOptions(items.map(product => productOptionFromEntity(product)));
    } catch {
        ElMessage.error('Failed to load products');
    } finally {
        productSearchLoading.value = false;
    }
};

const loadVariants = async (search: string) => {
    variantSearchLoading.value = true;

    try {
        const { items } = await fetchProductVariants({ search, page: 1, limit: 20 });

        mergeVariantOptions(items.map(variantOptionFromEntity));
    } catch {
        ElMessage.error('Failed to load variants');
    } finally {
        variantSearchLoading.value = false;
    }
};

const updateScope = (patch: Partial<DiscountScope>) => {
    emit('update:modelValue', { ...props.modelValue, ...patch });
    emit('change');
};
</script>

<style scoped>
.form-item-stack {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
}

.scope-field {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    width: 100%;
    margin-bottom: 8px;
}

.scope-field .cms-scope-select {
    width: 100%;
}

.scope-field :deep(.el-select__wrapper) {
    min-width: 0;
}

.scope-field :deep(.el-select__selected-item) {
    max-width: 100%;
}

.scope-field :deep(.el-select__tags-text) {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: min(160px, 45vw);
}
</style>
