/**
 * Composable d'erreurs partage des ecrans client (creation + edition, M1
 * Commercial). Factorise le cablage identique entre `clients/new.vue` et
 * `clients/[id]/edit.vue` (suggestion de revue ERP-101) :
 *  - un `useFormErrors` par groupe scalaire (Principal / Information /
 *    Comptabilite) : violations 422 affichees inline sous chaque champ ;
 *  - un tableau d'erreurs PAR LIGNE pour chaque collection (contacts /
 *    adresses / RIB), aligne sur l'index du `v-for`.
 *
 * `mapRowError` ne toaste PAS lui-meme : il retourne un booleen (true = mappe
 * inline). Chaque page conserve ainsi son propre fallback dans le `catch`
 * (toast generique en creation, `showError` en edition) sans imposer un
 * comportement commun.
 */
import { ref, type Ref } from 'vue'
import { mapViolationsToRecord } from '~/shared/utils/api'

export function useClientFormErrors() {
    const mainErrors = useFormErrors()
    const informationErrors = useFormErrors()
    const accountingErrors = useFormErrors()
    const contactErrors = ref<Record<string, string>[]>([])
    const addressErrors = ref<Record<string, string>[]>([])
    const ribErrors = ref<Record<string, string>[]>([])

    /**
     * Mappe l'erreur d'une ligne de collection sur le tableau cible (par index).
     * 422 avec violations exploitables → erreurs inline sous les champs de la
     * ligne + retourne true. Sinon → ne touche pas la cible et retourne false
     * (le caller decide du fallback toast).
     */
    function mapRowError(
        error: unknown,
        target: Ref<Record<string, string>[]>,
        index: number,
    ): boolean {
        const response = (error as { response?: { status?: number, _data?: unknown } })?.response
        const mapped = response?.status === 422 ? mapViolationsToRecord(response._data) : {}
        if (Object.keys(mapped).length > 0) {
            target.value[index] = mapped
            return true
        }
        return false
    }

    /**
     * Soumet TOUS les blocs d'une collection (contacts / adresses / RIB) en
     * collectant les erreurs par index : on n'arrete PAS au premier bloc en echec
     * (decision ERP-110 / ERP-101). Reinitialise le tableau d'erreurs cible, tente
     * chaque ligne via `saveRow`, mappe les 422 inline (mapRowError) ou delegue le
     * fallback a `onUnmappedError`. `shouldSkip` permet d'ignorer les blocs vides
     * (non remplis). Retourne true si au moins un bloc a echoue (le caller ne valide
     * alors pas l'onglet et n'affiche pas de toast succes).
     */
    async function submitRows<T>(
        rows: T[],
        target: Ref<Record<string, string>[]>,
        saveRow: (row: T, index: number) => Promise<void>,
        onUnmappedError: (error: unknown, index: number) => void,
        shouldSkip?: (row: T, index: number) => boolean,
    ): Promise<boolean> {
        target.value = []
        let hasError = false
        for (let index = 0; index < rows.length; index++) {
            // L'index reste borne par rows.length : la ligne existe forcement.
            const row = rows[index] as T
            if (shouldSkip?.(row, index)) {
                continue
            }
            try {
                await saveRow(row, index)
            }
            catch (error) {
                if (!mapRowError(error, target, index)) {
                    onUnmappedError(error, index)
                }
                hasError = true
            }
        }

        return hasError
    }

    return {
        mainErrors,
        informationErrors,
        accountingErrors,
        contactErrors,
        addressErrors,
        ribErrors,
        mapRowError,
        submitRows,
    }
}
