<script setup lang="ts">
import { useForm } from '@inertiajs/vue3';
import { Plus, Save } from '@lucide/vue';
import { ref } from 'vue';
import { toast } from 'vue-sonner';
import { Button } from '@/components/ui/button';
import { FieldError } from '@/components/ui/field';
import { useVariantList } from '@/composables/useVariantList';
import { formErrors } from '@/lib/form';
import { parseRupiah } from '@/lib/rupiah';
import { appendMediaToFormData, createMediaUploadState } from '@/types/media';
import type {
    EnumOption,
    RawMaterialPriceFormItem,
    RawMaterialUnit,
} from '@/types/raw-material';
import RawMaterialInfoSection from './RawMaterialInfoSection.vue';
import RawMaterialSharedPriceSection from './RawMaterialSharedPriceSection.vue';
import RawMaterialVariantSection from './RawMaterialVariantSection.vue';

const props = withDefaults(
    defineProps<{
        units: EnumOption[];
        initialData?: {
            name?: string;
            unit?: RawMaterialUnit;
            prices?: Array<{
                id?: number;
                variant?: string;
                price?: string;
                stock?: string;
                images?: Array<{ id: number; url: string; thumb_url: string }>;
            }>;
        };
        submitUrl: string;
        method?: 'post' | 'put';
        submitLabel?: string;
    }>(),
    {
        method: 'post',
        submitLabel: 'Simpan',
    },
);

function createClientId(): string {
    return `price-${crypto.randomUUID()}`;
}

const {
    items: prices,
    removeItem: removePrice,
    setField: setPriceField,
    appendToFormData,
    itemErrors: priceErrors,
} = useVariantList<RawMaterialPriceFormItem>(
    'prices',
    () => ({
        client_id: createClientId(),
        variant: '',
        price: '',
        stock: '0',
        media: createMediaUploadState(),
    }),
    () => {
        if (!props.initialData?.prices?.length) {
            return [{
                client_id: createClientId(),
                variant: '',
                price: '',
                stock: '0',
                media: createMediaUploadState(),
            }];
        }

        return props.initialData.prices.map((price) => ({
            client_id: createClientId(),
            id: price.id,
            variant: price.variant ?? '',
            price: price.price ?? '',
            stock: price.stock ?? '0',
            media: createMediaUploadState(price.images ?? []),
        }));
    },
);

const useSamePrice = ref(prices.value.length <= 1 || allPricesHaveSameValue());

const form = useForm({
    name: props.initialData?.name ?? '',
    unit: props.initialData?.unit ?? '',
});

function allPricesHaveSameValue(): boolean {
    if (prices.value.length <= 1) {
        return true;
    }

    const first = prices.value[0].price.trim();

    return prices.value.every((item) => item.price.trim() === first);
}

function addPrice() {
    const newPrice: RawMaterialPriceFormItem = {
        client_id: createClientId(),
        variant: '',
        price: '',
        stock: '0',
        media: createMediaUploadState(),
    };

    if (useSamePrice.value && prices.value[0]) {
        newPrice.price = prices.value[0].price;
    }

    prices.value = [...prices.value, newPrice];
}

function setPriceValue(clientId: string, value: string) {
    setPriceField(clientId, 'price', value);
}

function setSharedPrice(value: string) {
    prices.value = prices.value.map((price) => ({ ...price, price: value }));
}

function toggleUseSamePrice(checked: boolean) {
    useSamePrice.value = checked;

    if (!checked || !prices.value[0]) {
        return;
    }

    const sourcePrice = prices.value[0].price;
    prices.value = prices.value.map((price) => ({ ...price, price: sourcePrice }));
}

function applyPriceToAllVariants(sourceClientId: string) {
    const source = prices.value.find((price) => price.client_id === sourceClientId);

    if (!source) {
        return;
    }

    prices.value = prices.value.map((price) => ({ ...price, price: source.price }));
}

function parseStockValue(value: string): number {
    const parsed = Number.parseFloat(value.replace(',', '.'));

    return Number.isNaN(parsed) ? 0 : parsed;
}

function buildFormData(): FormData {
    const formData = new FormData();

    formData.append('name', form.name.trim());
    formData.append('unit', form.unit);

    appendToFormData(formData, (formData, index, price) => {
        formData.append(`prices[${index}][variant]`, price.variant.trim());
        formData.append(`prices[${index}][price]`, String(Number.parseInt(parseRupiah(price.price), 10) || 0));
        formData.append(`prices[${index}][stock]`, String(parseStockValue(price.stock)));
        appendMediaToFormData(formData, `prices[${index}]`, price.media);
    }, props.method);

    return formData;
}

function submit() {
    const payload = buildFormData();

    form.transform(() => payload).post(props.submitUrl, {
        forceFormData: true,
        onError: (errors: Record<string, string>) => {
            if (errors.system) {
                toast.error(errors.system);
            }
        },
    });
}
</script>

<template>
    <form @submit.prevent="submit">
        <div class="grid gap-6">
            <RawMaterialInfoSection :form="form" :units="units" :method="method" />

            <RawMaterialSharedPriceSection
                :form="form"
                :prices="prices"
                :use-same-price="useSamePrice"
                @toggle-use-same-price="toggleUseSamePrice"
                @set-shared-price="setSharedPrice"
            />

            <RawMaterialVariantSection
                v-for="(price, index) in prices"
                :key="price.client_id"
                :form="form"
                :price="price"
                :index="index"
                :total-prices="prices.length"
                :use-same-price="useSamePrice"
                :price-errors="(clientId, field) => priceErrors(form, clientId, field)"
                @remove="removePrice(price.client_id)"
                @apply-price-to-all="applyPriceToAllVariants(price.client_id)"
                @update:variant="setPriceField(price.client_id, 'variant', $event)"
                @update:stock="setPriceField(price.client_id, 'stock', $event)"
                @update:price="setPriceValue(price.client_id, $event)"
            />

            <FieldError :errors="formErrors(form, 'prices')" />

            <div class="flex items-center justify-between gap-2">
                <Button type="button" variant="outline" @click="addPrice">
                    <Plus class="size-4" />
                    Tambah Varian
                </Button>

                <Button type="submit" :disabled="form.processing">
                    <Save class="size-4" />
                    {{ form.processing ? 'Menyimpan...' : submitLabel }}
                </Button>
            </div>
        </div>
    </form>
</template>
