"use client"

import { useCallback, useEffect, useMemo, useState, useRef } from "react"
import { useFormik } from "formik"
import { enqueueSnackbar } from "notistack"
import { useTripDriverAssignmentStore } from "@/shared/api/stores/circuit-service/tripDriverAssignmentStore"
import { useCircuitStore } from "@/shared/api/stores/circuit-service/circuitStore"
import { ITripDriverAssignment, DriverScore, DriverSearchRequestDTO, DriverFilterType } from "@/shared/types/circuit"
import type { ModeType } from "@/shared/types/common"
import { driverAssignmentSchema } from "../../../../utils/circuit-validation"
import { getFormFieldsTripDriverAssignment, type Option } from "../../../../utils/form-fields-circuit"

interface UseChauffeurStepperLogicProps {
    chauffeur: ITripDriverAssignment
    mode: ModeType
    onClose: (isSaved: boolean) => void
    updateTabContent: (tabId: string, content: ITripDriverAssignment) => void
    tabId: string
    markTabAsSaved: (tabId: string, content: ITripDriverAssignment) => void
    hasUnsavedChanges?: boolean
    circuitId?: string
    circuitStartDate?: string
    circuitEndDate?: string
}

export type DriverFilter = DriverFilterType

const formatDateToISO = (dateString: string): string => {
    if (!dateString) return ""
    const date = new Date(dateString)
    const year = date.getFullYear()
    const month = String(date.getMonth() + 1).padStart(2, '0')
    const day = String(date.getDate()).padStart(2, '0')
    return `${year}-${month}-${day}`
}

export function useChauffeurStepperLogic({
    chauffeur,
    mode,
    onClose,
    updateTabContent,
    tabId,
    markTabAsSaved,
    hasUnsavedChanges,
    circuitId,
    circuitStartDate,
    circuitEndDate,
}: UseChauffeurStepperLogicProps) {
    const isEditMode = mode === "edit"
    const [driverOptions, setDriverOptions] = useState<Option[]>([])
    const [tripOptions, setTripOptions] = useState<Option[]>([])
    const [driverScores, setDriverScores] = useState<DriverScore[]>([])
    const [driverFilters, setDriverFilters] = useState<DriverFilterType[]>([DriverFilterType.ALL])
    const [searchRadius, setSearchRadius] = useState<number>(50)
    const [loadingMap, setLoadingMap] = useState(false)
    const [isAIMode, setIsAIMode] = useState(false)
    const [isSaving, setIsSaving] = useState(false)
    const prevChauffeurIdRef = useRef<string | undefined>(chauffeur.id)

    const {
        addTripDriverAssignmentForTrip,
        addTripDriverAssignmentForCircuit,
        addTripDriverAssignmentForMultipleTrips,
        updateTripDriverAssignment,
        searchDriversForMap,
        findBestDriverForTrip,
        findBestDriverForCircuit,
        findBestDriverForMultipleTrips,
        loading: LoadingDrivers,
    } = useTripDriverAssignmentStore()

    const { loading, getTripsByCircuitAndSchedule } = useCircuitStore()

    const initialValues = useMemo(() => {
        return {
            ...chauffeur,
            weeklyAssignmentSchedule: Array.isArray(chauffeur.weeklyAssignmentSchedule) ? chauffeur.weeklyAssignmentSchedule : [],
            assignmentEndDate: chauffeur.assignmentEndDate || circuitEndDate || '',
        }
    }, [chauffeur, circuitEndDate])

    const formik = useFormik<ITripDriverAssignment>({
        initialValues: initialValues,
        validationSchema: driverAssignmentSchema(circuitStartDate),
        onSubmit: (values) => {
            handleFormSubmit(values)
        },
        enableReinitialize: false,
        validateOnChange: true,
        validateOnBlur: true,
    })

    useEffect(() => {
        if (chauffeur.id !== prevChauffeurIdRef.current) {
            prevChauffeurIdRef.current = chauffeur.id
            formik.resetForm({ values: initialValues })
        }
    }, [chauffeur.id, initialValues])

    useEffect(() => {
        const { assignmentStartDate, assignmentEndDate } = formik.values

        if (formik.touched.assignmentStartDate && assignmentStartDate) {
            if (circuitStartDate && new Date(assignmentStartDate) < new Date(circuitStartDate)) {
                formik.setFieldError('assignmentStartDate', 'La date de début doit être égale ou postérieure à la date de début du circuit')
            } else if (assignmentEndDate && new Date(assignmentStartDate) > new Date(assignmentEndDate)) {
                formik.setFieldError('assignmentStartDate', 'La date de début doit être avant ou égale à la date de fin')
            }
        }

        if (formik.touched.assignmentEndDate && assignmentStartDate && assignmentEndDate) {
            if (new Date(assignmentStartDate) > new Date(assignmentEndDate)) {
                formik.setFieldError('assignmentEndDate', 'La date de fin doit être après ou égale à la date de début')
            }
        }
    }, [formik.values.assignmentStartDate, formik.values.assignmentEndDate, formik.touched.assignmentStartDate, formik.touched.assignmentEndDate, circuitStartDate])

    const fetchTripsWithDebounce = useCallback(
        async (circuitId: string, schedules: any[], startDate: string, endDate: string) => {
            if (!circuitId) {
                enqueueSnackbar("Circuit id non trouvé", { variant: "warning" })
                setTripOptions([])
                return
            }

            try {
                const formattedStartDate = startDate ? formatDateToISO(startDate) : ''
                const formattedEndDate = endDate ? formatDateToISO(endDate) : ''

                const trips = await getTripsByCircuitAndSchedule(circuitId, schedules, formattedStartDate, formattedEndDate)
                const tripOptionsData = trips.map((trip) => ({
                    value: trip.id.toString(),
                    label: trip.tripTitled || `Trajet ${trip.id}`,
                }))
                setTripOptions(tripOptionsData)
            } catch (error) {
                enqueueSnackbar("Erreur lors de la récupération des trajets", { variant: "error" })
                setTripOptions([])
            }
        },
        [getTripsByCircuitAndSchedule],
    )

    const loadDrivers = useCallback(async () => {
        if (!circuitId) {
            return
        }

        const { assignmentStartDate, assignmentEndDate, tripId } = formik.values

        if (!assignmentStartDate || !assignmentEndDate) {
            return
        }

        if (assignmentStartDate && assignmentEndDate && new Date(assignmentStartDate) > new Date(assignmentEndDate)) {
            return
        }

        try {
            setLoadingMap(true)

            const request: DriverSearchRequestDTO = {
                filterTypes: driverFilters,
                searchRadius: searchRadius,
                startDate: assignmentStartDate,
                endDate: assignmentEndDate,
                tripId: Array.isArray(tripId)
                    ? (tripId.length === 1 ? tripId[0] : undefined)
                    : tripId,
                circuitId: circuitId,
            }

            const driversMapResponse = await searchDriversForMap(request)

            const driverScoresList = driversMapResponse
                .filter((driver: any) => {
                    const hasIdentifier = driver.driverId !== null || (driver.email !== null && driver.email !== undefined)

                    const hasValidData =
                        hasIdentifier &&
                        driver.distanceFromCenter !== null &&
                        driver.distanceFromCenter !== undefined &&
                        typeof driver.distanceFromCenter === 'number' &&
                        driver.latitude !== null &&
                        driver.latitude !== undefined &&
                        typeof driver.latitude === 'number' &&
                        driver.longitude !== null &&
                        driver.longitude !== undefined &&
                        typeof driver.longitude === 'number'

                    return hasValidData
                })
                .map((driver: any, index: number) => ({
                    driverId: driver.driverId !== null ? String(driver.driverId) : driver.email,
                    driverName: driver.fullName || "Inconnu",
                    rank: index + 1,
                    emptyDistanceKm: driver.distanceFromCenter,
                    currentPosition: {
                        latitude: driver.latitude,
                        longitude: driver.longitude,
                    },
                    email: driver.email,
                    phoneNumber: driver.phoneNumber,
                    hasActiveCircuit: driver.hasActiveCircuit,
                    hasApplication: driver.hasApplication,
                    vehicle: driver.vehicle,
                    workingHours: driver.workingHours || [],
                    isCandidate: driver.driverId === null,
                }))

            setDriverScores(driverScoresList)

            const driverOptionsData = driverScoresList
                .filter((driver) => !driver.isCandidate)
                .map((driver) => ({
                    value: driver.driverId,
                    label: `${driver.driverName}${driver.hasApplication ? ' (Candidature validée)' : ''} - ${driver.emptyDistanceKm.toFixed(2)}km (#${driver.rank})`,
                    rank: driver.rank,
                    emptyDistanceKm: driver.emptyDistanceKm,
                }))
            setDriverOptions(driverOptionsData)

            if (driverScoresList.length === 0 && driversMapResponse.length > 0) {
                enqueueSnackbar("Aucun chauffeur/candidat avec coordonnées valides trouvé", { variant: "warning" })
            } else if (driverScoresList.length === 0) {
                enqueueSnackbar("Aucun chauffeur/candidat disponible pour ces critères", { variant: "info" })
            }

        } catch (error: any) {
            enqueueSnackbar(error.message || "Erreur lors du chargement des chauffeurs", { variant: "error" })
            setDriverOptions([])
            setDriverScores([])
        } finally {
            setLoadingMap(false)
        }
    }, [circuitId, formik.values.assignmentStartDate, formik.values.assignmentEndDate, formik.values.tripId, driverFilters, searchRadius, searchDriversForMap])

    const resetFilters = useCallback(() => {
        setDriverFilters([DriverFilterType.ALL])
        setSearchRadius(50)
        setIsAIMode(false)
    }, [])

    const switchToFilterMode = useCallback(() => {
        setIsAIMode(false)
        loadDrivers()
    }, [loadDrivers])

    const fetchBestDrivers = useCallback(async () => {
        if (!circuitId) {
            enqueueSnackbar("Circuit id non trouvé", { variant: "warning" })
            setDriverOptions([])
            setDriverScores([])
            return
        }

        const { assignmentStartDate, assignmentEndDate, weeklyAssignmentSchedule, tripId } = formik.values

        if (!assignmentStartDate || !assignmentEndDate) {
            enqueueSnackbar("Veuillez saisir les dates de début et de fin pour optimiser avec IA", { variant: "info" })
            return
        }

        if (!weeklyAssignmentSchedule || weeklyAssignmentSchedule.length === 0) {
            enqueueSnackbar("Veuillez sélectionner au moins un jour de la semaine", { variant: "info" })
            return
        }

        try {
            setLoadingMap(true)
            setIsAIMode(true)

            const trips = Array.isArray(tripId) ? tripId.filter(Boolean) : (tripId ? [tripId] : [])
            const isMultipleTrips = trips.length > 1

            const baseRequest = {
                circuitId,
                maxDrivers: 25,
                maxDistanceKm: searchRadius,
                startDate: assignmentStartDate,
                endDate: assignmentEndDate,
                schedules: weeklyAssignmentSchedule,
            }

            let iaRanking: DriverScore[] = []
            if (isMultipleTrips) {
                const response = await findBestDriverForMultipleTrips({ ...baseRequest, tripIds: trips })
                iaRanking = (response?.globalRanking || []).map((r: any) => ({
                    driverId: String(r.driverId),
                    driverName: r.driverName,
                    rank: r.rank,
                    emptyDistanceKm: Number(r.emptyDistanceKm ?? 0),
                    vehicle: r.vehicleInfo ?? null,
                    homeLat: typeof r.homeLat === 'number' ? r.homeLat : undefined,
                    homeLng: typeof r.homeLng === 'number' ? r.homeLng : undefined,
                    currentPosition: (typeof r.homeLat === 'number' && typeof r.homeLng === 'number')
                        ? { latitude: r.homeLat, longitude: r.homeLng }
                        : undefined,
                    workingHours: Array.isArray(r.workingHours) ? r.workingHours.map((wh: any) => ({
                        id: wh.id,
                        dayOfWeek: wh.dayOfWeek,
                        startHour: wh.startHour,
                        endHour: wh.endHour,
                    })) : [],
                    isCandidate: false,
                    hasApplication: false,
                }))
            } else if (trips.length === 1) {
                const response = await findBestDriverForTrip({ ...baseRequest, tripId: trips[0] })
                iaRanking = (response?.rankedDrivers || []).map((r: any) => ({
                    driverId: String(r.driverId),
                    driverName: r.driverName,
                    rank: r.rank,
                    emptyDistanceKm: Number(r.emptyDistanceKm ?? 0),
                    vehicle: r.vehicleInfo ?? null,
                    homeLat: typeof r.homeLat === 'number' ? r.homeLat : undefined,
                    homeLng: typeof r.homeLng === 'number' ? r.homeLng : undefined,
                    currentPosition: (typeof r.homeLat === 'number' && typeof r.homeLng === 'number')
                        ? { latitude: r.homeLat, longitude: r.homeLng }
                        : undefined,
                    workingHours: Array.isArray(r.workingHours) ? r.workingHours.map((wh: any) => ({
                        id: wh.id,
                        dayOfWeek: wh.dayOfWeek,
                        startHour: wh.startHour,
                        endHour: wh.endHour,
                    })) : [],
                    isCandidate: false,
                    hasApplication: false,
                }))
            } else {
                const response = await findBestDriverForCircuit(baseRequest)
                iaRanking = (response?.globalRanking || []).map((r: any) => ({
                    driverId: String(r.driverId),
                    driverName: r.driverName,
                    rank: r.rank,
                    emptyDistanceKm: Number(r.emptyDistanceKm ?? 0),
                    vehicle: r.vehicleInfo ?? null,
                    homeLat: typeof r.homeLat === 'number' ? r.homeLat : undefined,
                    homeLng: typeof r.homeLng === 'number' ? r.homeLng : undefined,
                    currentPosition: (typeof r.homeLat === 'number' && typeof r.homeLng === 'number')
                        ? { latitude: r.homeLat, longitude: r.homeLng }
                        : undefined,
                    workingHours: Array.isArray(r.workingHours) ? r.workingHours.map((wh: any) => ({
                        id: wh.id,
                        dayOfWeek: wh.dayOfWeek,
                        startHour: wh.startHour,
                        endHour: wh.endHour,
                    })) : [],
                    isCandidate: false,
                    hasApplication: false,
                }))
            }

            setDriverScores(iaRanking)

            const driverOptionsData = iaRanking
                .filter((driver) => !driver.hasApplication)
                .map((driver) => ({
                    value: driver.driverId,
                    label: `${driver.driverName} - ${driver.emptyDistanceKm.toFixed(2)}km (#${driver.rank})`,
                    rank: driver.rank,
                    emptyDistanceKm: driver.emptyDistanceKm,
                }))
            setDriverOptions(driverOptionsData)

            if (iaRanking.length === 0) {
                enqueueSnackbar("Aucun chauffeur disponible pour ces critères", { variant: "info" })
            } else {
                enqueueSnackbar(`${iaRanking.length} chauffeur(s) trouvé(s) avec IA`, { variant: "success" })
            }
        } catch (error: any) {
            enqueueSnackbar(error.message || "Erreur lors de la récupération des chauffeurs", { variant: "error" })
            setDriverOptions([])
            setDriverScores([])
        } finally {
            setLoadingMap(false)
        }
    }, [circuitId, formik.values.assignmentStartDate, formik.values.assignmentEndDate, formik.values.weeklyAssignmentSchedule, formik.values.tripId, searchRadius, findBestDriverForTrip, findBestDriverForCircuit, findBestDriverForMultipleTrips])

    const handleFormSubmit = async (values: ITripDriverAssignment) => {
        setIsSaving(true)
        try {
            if (isEditMode) {
                await updateTripDriverAssignment(values)
                enqueueSnackbar("Chauffeur modifié avec succès", { variant: "success" })
                markTabAsSaved(tabId, values)
                formik.resetForm({ values })
            } else if (mode === "add" || mode === "copy") {
                const { id, createdAt, updatedAt, ...valuesWithoutId } = values

                const tripId = valuesWithoutId.tripId
                const isMultipleTrips = Array.isArray(tripId) && tripId.length > 1

                let assignmentStartDate = valuesWithoutId.assignmentStartDate
                if (circuitStartDate && assignmentStartDate && new Date(assignmentStartDate) < new Date(circuitStartDate)) {
                    assignmentStartDate = circuitStartDate
                }

                const dataToSend = {
                    ...valuesWithoutId,
                    assignmentStartDate,
                    tripId: isMultipleTrips ? undefined : (Array.isArray(tripId) ? tripId[0] : tripId),
                    tripIds: isMultipleTrips ? tripId : undefined,
                }

                if (isMultipleTrips) {
                    await addTripDriverAssignmentForMultipleTrips(dataToSend as any)
                    enqueueSnackbar(`Affectations créées pour ${tripId.length} trajets`, { variant: "success" })
                } else if (dataToSend.tripId && circuitId) {
                    await addTripDriverAssignmentForTrip(dataToSend as any)
                    enqueueSnackbar("Affectation chauffeur-trajet ajoutée avec succès", { variant: "success" })
                } else if (circuitId) {
                    await addTripDriverAssignmentForCircuit(dataToSend as any, circuitId)
                    enqueueSnackbar("Affectation chauffeur-circuit ajoutée avec succès", { variant: "success" })
                } else {
                    throw new Error("CircuitId requis pour l'ajout d'affectation")
                }

                setDriverScores([])
                setDriverOptions([])
                setLoadingMap(false)
                setIsAIMode(false)
                
                onClose(true)
            }
        } catch (error: any) {
            enqueueSnackbar(error.message || "Erreur lors de l'enregistrement", { variant: "error" })
        }
    }

    const formFields = useMemo(() => {
        return getFormFieldsTripDriverAssignment(tripOptions, driverOptions)
    }, [tripOptions, driverOptions])

    const filteredDriverScores = useMemo(() => {
        return driverScores
    }, [driverScores])

    const handleSave = () => {
        formik.validateForm().then((errors) => {
            formik.submitForm()
        })
    }

    useEffect(() => {
        if (isAIMode || isSaving) return

        const { assignmentStartDate, assignmentEndDate } = formik.values

        if (circuitStartDate && assignmentStartDate && new Date(assignmentStartDate) < new Date(circuitStartDate)) {
            return
        }

        if (assignmentStartDate && assignmentEndDate && new Date(assignmentStartDate) > new Date(assignmentEndDate)) {
            return
        }

        if (!assignmentStartDate || !assignmentEndDate) {
            if (formik.touched.assignmentStartDate || formik.touched.assignmentEndDate) {
                enqueueSnackbar("Veuillez saisir les dates de début et de fin pour charger les chauffeurs", { variant: "info" })
            }
            return
        }

        const timer = setTimeout(() => {
            loadDrivers()
        }, 300)

        return () => clearTimeout(timer)
    }, [driverFilters, searchRadius, formik.values.assignmentStartDate, formik.values.assignmentEndDate, formik.values.tripId, isAIMode, isSaving, circuitStartDate, loadDrivers, formik.touched.assignmentStartDate, formik.touched.assignmentEndDate])

    useEffect(() => {
        if (!isAIMode || isSaving) return

        const timer = setTimeout(() => {
            fetchBestDrivers()
        }, 500)

        return () => clearTimeout(timer)
    }, [searchRadius, isAIMode, isSaving, fetchBestDrivers])

    useEffect(() => {
        const schedules = formik.values.weeklyAssignmentSchedule
        const startDate = formik.values.assignmentStartDate
        const endDate = formik.values.assignmentEndDate

        if (!circuitId || isSaving) return

        const timer = setTimeout(() => {
            fetchTripsWithDebounce(circuitId, schedules, startDate, endDate)
        }, 800)

        return () => {
            clearTimeout(timer)
        }
    }, [formik.values.weeklyAssignmentSchedule, formik.values.assignmentStartDate, formik.values.assignmentEndDate, circuitId, isSaving, fetchTripsWithDebounce])

    useEffect(() => {
        if (updateTabContent && tabId && mode !== "view" && !isSaving) {
            const timer = setTimeout(() => {
                updateTabContent(tabId, formik.values)
            }, 300)

            return () => {
                clearTimeout(timer)
            }
        }
    }, [formik.values, updateTabContent, tabId, mode, isSaving])

    return {
        driverOptions,
        tripOptions,
        LoadingDrivers,
        loading,
        formik,
        formFields,
        handleSave,
        driverScores: filteredDriverScores,
        driverFilters,
        setDriverFilters,
        fetchBestDrivers,
        loadingMap,
        searchRadius,
        setSearchRadius,
        isAIMode,
        resetFilters,
        switchToFilterMode,
    }
}