import { useCallback, useMemo, useRef, useEffect, useState } from 'react'
import { toast } from 'react-toastify'
import { IScenario, IUseScenario } from '../screens/BookingForm/types'
import { useBookingContext } from '../providers/BookingFormProvider/BookingFormProvider'
import { useWording } from './useWording'
import { useDispatch } from '@wordpress/data'
import { store_name } from '../../store/frontend'
import { stripeMethods } from '../screens/BookingForm/PaymentHandler/payments/Stripe/StripeMethods'
import { trackBookingFormSubmitted } from '../utils/frontendAnalytics'

export const useScenario = (scenarios: IScenario[]) => {
    const {
        formData,
        services,
        units,
        bookingMode,
        paymentMethods,
        preset,
        stripeObj,
        amountData,
        loading,
    } = useBookingContext()
    const wording = useWording()
    const filteredScenarios = useMemo(
        () =>
            scenarios.filter(({ isVisible }: IScenario) => {
                if (isVisible === undefined) return true
                return isVisible()
            }),
        [scenarios, paymentMethods]
    )

    // Always keep the latest filteredScenarios in a ref
    const filteredScenariosRef = useRef(filteredScenarios)
    useEffect(() => {
        filteredScenariosRef.current = filteredScenarios
    }, [filteredScenarios])

    const [currentIndex, setCurrentIndex] = useState(0)
    const currentIndexRef = useRef(0)
    useEffect(() => {
        currentIndexRef.current = currentIndex
    }, [currentIndex])

    const currentScenario = filteredScenarios[currentIndex]
    const { placeBooking, setLoading, submitStripePayment, setBookingData } =
        useDispatch(store_name)

    const title =
        typeof currentScenario.title === 'string'
            ? currentScenario.title === 'select_services' &&
              bookingMode === 'units'
                ? wording?.select_units ||
                  wording?.[currentScenario.title] ||
                  currentScenario.title
                : wording?.[currentScenario.title] || currentScenario.title
            : currentScenario.title
    const description =
        typeof currentScenario.description === 'string'
            ? currentScenario.description === 'choose_service_proceed' &&
              bookingMode === 'units'
                ? wording?.choose_unit_proceed ||
                  wording?.[currentScenario.description] ||
                  currentScenario.description
                : wording?.[currentScenario.description] ||
                  currentScenario.description
            : currentScenario.description

    const { canGoNext, nextStepError } = useMemo(() => {
        const isCheckoutStep = currentScenario?.title === 'personal_details'
        if (isCheckoutStep && loading?.bookingFields) {
            return {
                canGoNext: false,
                nextStepError: wording?.loading || null,
            }
        }
        let firstError: string | null = null
        const allValid = Object.entries(currentScenario.validationRules).every(
            ([field, validator]) => {
                const value = formData[field]
                const result =
                    value !== undefined && value !== null
                        ? validator(value)
                        : validator(formData)
                if (result !== true && !firstError) {
                    firstError = typeof result === 'string' ? result : null
                }
                return result === true
            }
        )
        return {
            canGoNext: allValid,
            nextStepError:
                (typeof firstError === 'string' && wording?.[firstError]) ||
                firstError,
        }
    }, [formData, currentScenario, stripeObj?.isValidated, amountData, loading?.bookingFields])

    const canGoNextRef = useRef(canGoNext)
    const formDataRef = useRef(formData)

    useEffect(() => {
        canGoNextRef.current = canGoNext
    }, [canGoNext])

    useEffect(() => {
        formDataRef.current = formData
    }, [formData])

    const jumpToTop = useCallback(() => {
        setTimeout(() => {
            let targetForm: HTMLElement | null = null

            const forms = Array.from(
                document.querySelectorAll<HTMLElement>('.webba_booking_form_v6')
            )
            for (const form of forms) {
                const button = form.querySelector<HTMLButtonElement>(
                    '.wbk_navigation__button-next'
                )
                if (button && button.offsetParent !== null) {
                    targetForm = form
                    break
                }
            }

            if (!targetForm && forms.length > 0) {
                targetForm = forms[0]
            }

            if (targetForm) {
                targetForm.scrollIntoView({
                    behavior: 'smooth',
                    block: 'start',
                })
            } else {
                window.scrollTo({ top: 30, behavior: 'smooth' })
            }
        }, 50)
    }, [])

    const goBack = useCallback(() => {
        setCurrentIndex((prev) => Math.max(prev - 1, 0))
        jumpToTop()
    }, [jumpToTop])

    const processBooking = useCallback(async () => {
        const submittedFormData = formDataRef.current
        const itemsCount =
            bookingMode === 'units'
                ? submittedFormData.units?.length ?? 0
                : submittedFormData.services?.length ?? 0
        const baseTrackingProperties = {
            booking_mode: bookingMode,
            items_count: itemsCount,
            payment_method: submittedFormData.payment_method,
            has_coupon: Boolean(submittedFormData.coupon?.trim()),
            total_amount: amountData?.to_pay_total,
        }

        let response: any
        try {
            response = await placeBooking(submittedFormData)
        } catch (e: any) {
            const message =
                e?.message ??
                e?.data?.message ??
                (typeof e?.data === 'string' ? e.data : null) ??
                (e?.code ? String(e.code) : null) ??
                'Booking failed'

            trackBookingFormSubmitted({
                ...baseTrackingProperties,
                success: false,
                error_message: message,
            })

            toast.error(message)
            throw e
        }

        trackBookingFormSubmitted({
            ...baseTrackingProperties,
            success: true,
            payment_required: response?.payment_required,
        })

        if (!stripeObj) return response

        const { stripe, elements } = stripeObj

        if (!stripe || !elements) return response

        if (
            stripeMethods
                .filter(
                    (method) => !['google_pay', 'apple_pay'].includes(method)
                )
                .includes(formDataRef.current?.payment_method)
        ) {
            setLoading('createBooking', true)

            const redirectUrl =
                preset?.settings?.stripe_redirect_url || window.location.href
            const { error, paymentIntent } = await stripe.confirmPayment({
                elements,
                confirmParams: {
                    return_url: `${redirectUrl}?paymentMethod=${formDataRef.current?.payment_method}&`,
                    payment_method_data: {
                        billing_details: {
                            name: formDataRef.current?.stripe_details
                                ?.billing_details?.name,
                            address: {
                                line1: formDataRef.current?.stripe_details
                                    ?.billing_details?.line1,
                                line2: formDataRef.current?.stripe_details
                                    ?.billing_details?.line2,
                                city: formDataRef.current?.stripe_details
                                    ?.billing_details?.city,
                                state: formDataRef.current?.stripe_details
                                    ?.billing_details?.state,
                                postal_code:
                                    formDataRef.current?.stripe_details
                                        ?.billing_details?.postal_code,
                                country:
                                    formDataRef.current?.stripe_details
                                        ?.billing_details?.country,
                            },
                        },
                    },
                },
                redirect: 'if_required',
            })

            if (paymentIntent?.status === 'succeeded') {
                const paymentResponse = await submitStripePayment(
                    paymentIntent.id,
                    formDataRef.current?.payment_method
                )

                if (paymentResponse.status === 'success') {
                    setBookingData({
                        payment_required: false,
                    })
                } else {
                    alert(paymentResponse?.message || 'Something went wrong')
                }
            } else if (error) {
                alert(error.message)
            } else {
                alert('Something went wrong')
            }

            setLoading('createBooking', false)
        }

        return response
    }, [
        stripeObj,
        placeBooking,
        stripeMethods,
        formDataRef,
        bookingMode,
        amountData,
        setLoading,
        submitStripePayment,
        setBookingData,
    ])

    const goNext = useCallback(async () => {
        if (!canGoNextRef.current) return

        const index = currentIndexRef.current
        const scenarios = filteredScenariosRef.current

        if (index === scenarios.length - 1) {
            const response = await processBooking()

            jumpToTop()
            return
        }

        setCurrentIndex((prev) => Math.min(prev + 1, scenarios.length - 1))
        jumpToTop()
    }, [placeBooking, jumpToTop, processBooking])

    const stepInto = useCallback((index: number) => {
        setCurrentIndex(index)
    }, [])

    useEffect(() => {
        if (bookingMode === 'units') {
            const selectedUnits = (units || []).filter((u) => u.selected)
            if (!selectedUnits.length) {
                setCurrentIndex(0)
            }
            return
        }
        const selectedServices = services.filter((service) => service.selected)
        if (!selectedServices || selectedServices.length === 0) {
            setCurrentIndex(0)
        }
    }, [services, units, bookingMode])

    return {
        ...currentScenario,
        title,
        description,
        totalSteps: filteredScenarios.length,
        currentIndex,
        canGoNext,
        goBack,
        goNext,
        stepInto,
        nextStepError,
    } as IUseScenario
}
