import { type FormDataConvertible } from '@inertiajs/core';
import { router } from '@inertiajs/react';
import { useCallback, useState } from 'react';
import { toast } from 'sonner';
import type { BulkFailure, BulkResult } from './types';

const CHUNK_SIZE = 5;

export type FieldKey = 'price' | 'cancellation_policies' | 'additional_fees' | 'check_in_out' | 'preparation_time';

export type BulkJob = { field: FieldKey; value: Record<string, FormDataConvertible> };

function chunkArray<T>(arr: T[], size: number): T[][] {
    const out: T[][] = [];
    for (let i = 0; i < arr.length; i += size) {
        out.push(arr.slice(i, i + size));
    }
    return out;
}

/**
 * Engine of the bulk-edit page: applies one or more jobs (field + value) to a
 * list of property uids via sequential chunked POSTs to logements.bulkUpdate,
 * accumulating per-property results from flash.bulk_result.
 *
 * total = uids.length × jobs.length (each job is pushed to every property).
 */
export default function useBulkApply() {
    const [running, setRunning] = useState(false);
    const [finished, setFinished] = useState(false);
    const [processed, setProcessed] = useState(0);
    const [total, setTotal] = useState(0);
    const [succeeded, setSucceeded] = useState<string[]>([]);
    const [failed, setFailed] = useState<BulkFailure[]>([]);

    const reset = useCallback(() => {
        setRunning(false);
        setFinished(false);
        setProcessed(0);
        setTotal(0);
        setSucceeded([]);
        setFailed([]);
    }, []);

    const start = useCallback((jobs: BulkJob[], uids: string[]) => {
        if (jobs.length === 0 || uids.length === 0) {
            return;
        }

        const chunks = chunkArray(uids, CHUNK_SIZE);
        setRunning(true);
        setFinished(false);
        setProcessed(0);
        setTotal(uids.length * jobs.length);
        setSucceeded([]);
        setFailed([]);

        let okAcc: string[] = [];
        let failAcc: BulkFailure[] = [];
        let done = 0;

        const postChunk = (jobIndex: number, chunkIndex: number) => {
            if (chunkIndex >= chunks.length) {
                if (jobIndex + 1 < jobs.length) {
                    postChunk(jobIndex + 1, 0);
                    return;
                }
                setRunning(false);
                setFinished(true);
                if (failAcc.length === 0) {
                    toast.success(`${new Set(okAcc).size} logement(s) mis à jour.`);
                } else {
                    toast.warning(`${new Set(okAcc).size} mis à jour, ${failAcc.length} en échec.`);
                }
                return;
            }

            const job = jobs[jobIndex];
            const currentChunk = chunks[chunkIndex];

            router.post(
                route('logements.bulkUpdate'),
                { property_uids: currentChunk, field: job.field, value: job.value },
                {
                    preserveState: true,
                    preserveScroll: true,
                    only: ['flash'],
                    onSuccess: (page) => {
                        const result = (page.props as { flash?: { bulk_result?: BulkResult } }).flash?.bulk_result;
                        if (result) {
                            okAcc = [...okAcc, ...(result.succeeded ?? [])];
                            failAcc = [...failAcc, ...(result.failed ?? [])];
                            if (result.fatal) {
                                failAcc = [...failAcc, ...currentChunk.map((uid) => ({ uid, name: null, reason: result.fatal as string }))];
                            }
                        }
                        done += currentChunk.length;
                        setProcessed(done);
                        setSucceeded(okAcc);
                        setFailed(failAcc);
                        postChunk(jobIndex, chunkIndex + 1);
                    },
                    onError: () => {
                        failAcc = [...failAcc, ...currentChunk.map((uid) => ({ uid, name: null, reason: 'Erreur de validation.' }))];
                        done += currentChunk.length;
                        setProcessed(done);
                        setFailed(failAcc);
                        postChunk(jobIndex, chunkIndex + 1);
                    },
                },
            );
        };

        postChunk(0, 0);
    }, []);

    return { running, finished, processed, total, succeeded, failed, start, reset };
}
