<template>
    <form @submit.prevent="validate">
        <div class="flex items-center justify-between gap-10">
            <div>
                <h1 class="text-3xl font-bold uppercase">
                    {{ addressId ? "Modification d'une adresse" : "Ajout d'une adresse" }}
                </h1>
                <p v-if="entityName" class="text-sm text-slate-500 mt-1">
                    {{ entityLabel }} : {{ entityName }}
                </p>
            </div>

            <button
                class="text-xl uppercase bg-primary-500 text-white h-[50px] w-[272px]"
                type="submit"
                :disabled="isLoading || entityId === null"
            >
                {{ addressId ? "Sauvegarder" : "Ajouter" }}
            </button>
        </div>

        <div class="grid grid-cols-2 gap-y-16 gap-x-12 mb-16 mt-10">
            <UiTextInput id="address-label" v-model="form.label" label="Libellé" />
            <UiTextInput id="address-street" v-model="form.street" label="Rue" />
            <UiTextInput id="address-street2" v-model="form.street2" label="Complément" />
            <UiTextInput id="address-postalCode" v-model="form.postalCode" label="Code postal" />
            <UiTextInput id="address-city" v-model="form.city" label="Ville" />
            <UiTextInput id="address-country" v-model="form.countryCode" label="Pays" />
        </div>

        <p v-if="errorMsg" class="text-red-600 mt-4">{{ errorMsg }}</p>
    </form>
</template>

<script setup lang="ts">
import { computed, reactive, ref, watch } from "vue"
import { createAddress, getAddress, updateAddress, type AddressPayload } from "~/services/address"
import { getSupplier, updateSupplier } from "~/services/supplier"
import type { SupplierPayload } from "~/services/dto/supplier-data"

const route = useRoute()
const router = useRouter()
const { $i18n } = useNuxtApp()

type AddressOwner = {
    id: number
    name?: string | null
    email?: string | null
    phone?: string | null
    addresses?: unknown
}

type AddressOwnerAdapter = {
    loadEntity: (id: number) => Promise<AddressOwner>
    updateEntity: (id: number, payload: unknown) => Promise<unknown>
    buildUpdatePayload: (owner: AddressOwner | null, addresses: string[]) => unknown
    getEntityName: (owner: AddressOwner | null) => string
    normalizeAddresses: (addresses: AddressOwner["addresses"]) => string[]
}

const props = defineProps<{
    entityType?: "supplier" | "customer"
    entityId?: number | null
    returnPath?: string
    adapter?: Partial<AddressOwnerAdapter>
}>()

const resolveId = (param: unknown) => {
    const idStr = Array.isArray(param) ? param[0] : param
    if (!idStr) return null
    const id = Number(idStr)
    return Number.isFinite(id) ? id : null
}

const entityType = computed<"supplier" | "customer">(() => {
    if (props.entityType) return props.entityType
    const queryType = route.query.entityType
    if (queryType === "customer" || queryType === "supplier") return queryType
    const path = route.path
    if (path.includes("/customer/")) return "customer"
    return "supplier"
})

const entityId = computed(() => {
    if (props.entityId !== undefined) return props.entityId ?? null
    const typeKey = `${entityType.value}Id`
    return resolveId(route.query[typeKey]) ?? resolveId(route.query.entityId)
})
const addressId = computed(() => resolveId(route.query.addressId))

const entity = ref<AddressOwner | null>(null)
const entityName = computed(() => activeAdapter.value.getEntityName(entity.value))
const entityLabel = computed(() => (entityType.value === "customer" ? "Client" : "Fournisseur"))

const isLoading = ref(false)
const errorMsg = ref<string | null>(null)

const form = reactive<AddressPayload>({
    label: "",
    street: "",
    street2: null,
    postalCode: "",
    city: "",
    countryCode: "",
})

const defaultSupplierAdapter: AddressOwnerAdapter = {
    loadEntity: getSupplier,
    updateEntity: updateSupplier,
    buildUpdatePayload: (owner, addresses) => ({
        name: owner?.name ?? "",
        email: owner?.email ?? null,
        phone: owner?.phone ?? null,
        addresses,
    } satisfies SupplierPayload),
    getEntityName: (owner) => owner?.name ?? "",
    normalizeAddresses: (value) => {
        if (!Array.isArray(value) || value.length === 0) return []
        if (typeof value[0] === "string") {
            return value.filter((item): item is string => typeof item === "string")
        }
        return value
            .map((address) =>
                address && typeof address === "object" && "id" in address && address.id
                    ? `/api/addresses/${address.id}`
                    : null
            )
            .filter((item): item is string => Boolean(item))
    },
}

const activeAdapter = computed<AddressOwnerAdapter>(() => ({
    ...defaultSupplierAdapter,
    ...props.adapter,
}))

watch(
    () => [entityId.value, addressId.value] as const,
    async ([entityIdValue, addressIdValue]) => {
        if (entityIdValue === null) {
            entity.value = null
            return
        }
        isLoading.value = true
        try {
            entity.value = await activeAdapter.value.loadEntity(entityIdValue)
            if (addressIdValue !== null) {
                const address = await getAddress(addressIdValue)
                form.label = address.label ?? ""
                form.street = address.street ?? ""
                form.street2 = address.street2 ?? null
                form.postalCode = address.postalCode ?? ""
                form.city = address.city ?? ""
                form.countryCode = address.countryCode ?? ""
            } else {
                form.label = ""
                form.street = ""
                form.street2 = null
                form.postalCode = ""
                form.city = ""
                form.countryCode = ""
            }
        } finally {
            isLoading.value = false
        }
    },
    { immediate: true }
)

async function validate() {
    if (isLoading.value) return
    if (entityId.value === null) {
        errorMsg.value = String($i18n.t("errors.address.entityNotFound"))
        return
    }
    errorMsg.value = null
    isLoading.value = true

    try {
        if (!entity.value) {
            entity.value = await activeAdapter.value.loadEntity(entityId.value)
        }

        const payload: AddressPayload = {
            label: form.label.trim() || entityName.value || "Adresse",
            street: form.street.trim(),
            street2: form.street2?.trim() || null,
            postalCode: form.postalCode.trim(),
            city: form.city.trim(),
            countryCode: form.countryCode.trim().toUpperCase(),
        }
        if (!payload.street) {
            errorMsg.value = String($i18n.t("errors.address.streetRequired"))
            return
        }
        if (!payload.postalCode) {
            errorMsg.value = String($i18n.t("errors.address.postalCodeRequired"))
            return
        }
        if (!payload.city) {
            errorMsg.value = String($i18n.t("errors.address.cityRequired"))
            return
        }
        if (payload.countryCode.length !== 2) {
            errorMsg.value = String($i18n.t("errors.address.countryCodeInvalid"))
            return
        }

        let updatedAddressId = addressId.value
        if (addressId.value !== null) {
            const updated = await updateAddress(addressId.value, payload)
            updatedAddressId = updated.id
        } else {
            const created = await createAddress(payload)
            updatedAddressId = created.id
        }

        if (updatedAddressId !== null) {
            const existingAddresses = activeAdapter.value.normalizeAddresses(entity.value?.addresses ?? [])
            const updatedAddresses = existingAddresses.includes(`/api/addresses/${updatedAddressId}`)
                ? existingAddresses
                : [...existingAddresses, `/api/addresses/${updatedAddressId}`]

            const updatePayload = activeAdapter.value.buildUpdatePayload(entity.value, updatedAddresses)
            await activeAdapter.value.updateEntity(entityId.value, updatePayload)
        }
        const returnPath =
            props.returnPath ?? `/admin/${entityType.value}/${entityId.value}`
        await router.push(returnPath)
    } catch (e) {
        const errorKey = addressId.value !== null ? "errors.address.update" : "errors.address.create"
        errorMsg.value = String($i18n.t(errorKey))
        throw e
    } finally {
        isLoading.value = false
    }
}
</script>
