import { router, usePage } from '@inertiajs/react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useOptionalSidebar } from '@/components/ui/sidebar';
import {
    TOUR_VERSION,
    TOUR_WELCOME_DELAY_MS,
} from '@/lib/application-tour/constants';
import { TOUR_STEPS  } from '@/lib/application-tour/steps';
import type {TourStep} from '@/lib/application-tour/steps';
import { isOnRoute, waitForTarget } from '@/lib/application-tour/target';
import { update as updateTour } from '@/routes/application-tour';
import type { SharedPageProps } from '@/types/constants';

export type TourPhase = 'idle' | 'welcome' | 'active' | 'completion';

type SidebarSnapshot = {
    open: boolean;
    openMobile: boolean;
};

export function useApplicationTour() {
    const { auth } = usePage<SharedPageProps>().props;
    const sidebar = useOptionalSidebar();
    const replayButtonRef = useRef<HTMLButtonElement>(null);
    const sidebarSnapshotRef = useRef<SidebarSnapshot | null>(null);

    const [phase, setPhase] = useState<TourPhase>('idle');
    const [stepIndex, setStepIndex] = useState(0);
    const [steps, setSteps] = useState<TourStep[]>([]);
    const [isPreparingStep, setIsPreparingStep] = useState(false);

    const tourCompletedForVersion =
        auth.applicationTour?.version === TOUR_VERSION &&
        auth.applicationTour.status !== null;

    const showSkippedBadge =
        auth.applicationTour?.status === 'skipped' &&
        !tourCompletedForVersion;

    const currentStep = steps[stepIndex] ?? null;
    const isLastStep = stepIndex === steps.length - 1;
    const isTourActive = phase === 'active';

    const persistTour = useCallback((status: 'completed' | 'skipped') => {
        router.post(
            updateTour.url(),
            { status, version: TOUR_VERSION },
            { preserveScroll: true, preserveState: true },
        );
    }, []);

    const restoreSidebar = useCallback(() => {
        const snapshot = sidebarSnapshotRef.current;

        if (!sidebar || !snapshot) {
            return;
        }

        sidebar.setOpen(snapshot.open);
        sidebar.setOpenMobile(snapshot.openMobile);
        sidebarSnapshotRef.current = null;
    }, [sidebar]);

    const captureSidebar = useCallback(() => {
        if (!sidebar || sidebarSnapshotRef.current) {
            return;
        }

        sidebarSnapshotRef.current = {
            open: sidebar.open,
            openMobile: sidebar.openMobile,
        };
    }, [sidebar]);

    const prepareSidebarForStep = useCallback(
        (step: TourStep) => {
            if (!sidebar || !step.requiresSidebar) {
                return;
            }

            captureSidebar();

            if (sidebar.isMobile) {
                sidebar.setOpenMobile(true);
            } else if (sidebar.state === 'collapsed') {
                sidebar.setOpen(true);
            }
        },
        [captureSidebar, sidebar],
    );

    const prepareStep = useCallback(
        async (step: TourStep): Promise<boolean> => {
            prepareSidebarForStep(step);

            if (step.route && !isOnRoute(step.route)) {
                await new Promise<void>((resolve) => {
                    router.visit(step.route!, {
                        preserveScroll: true,
                        preserveState: true,
                        onFinish: () => resolve(),
                    });
                });
            }

            const target = await waitForTarget(step.selector);

            if (!target) {
                if (import.meta.env.DEV) {
                    console.warn(
                        `[ApplicationTour] Target not found: ${step.selector}`,
                    );
                }

                return false;
            }

            target.scrollIntoView({
                behavior: window.matchMedia('(prefers-reduced-motion: reduce)')
                    .matches
                    ? 'auto'
                    : 'smooth',
                block: 'center',
            });

            return true;
        },
        [prepareSidebarForStep],
    );

    const resolveAvailableSteps = useCallback(async (): Promise<TourStep[]> => {
        const available: TourStep[] = [];

        for (const step of TOUR_STEPS) {
            if (step.route && !isOnRoute(step.route)) {
                available.push(step);

                continue;
            }

            if (document.querySelector(step.selector)) {
                available.push(step);
            }
        }

        return available;
    }, []);

    const endTour = useCallback(
        (status: 'completed' | 'skipped', showCompletion = false) => {
            restoreSidebar();
            setPhase(showCompletion ? 'completion' : 'idle');
            setStepIndex(0);
            setSteps([]);
            persistTour(status);

            window.setTimeout(() => {
                replayButtonRef.current?.focus();
            }, 0);
        },
        [persistTour, restoreSidebar],
    );

    const startTour = useCallback(async () => {
        const availableSteps = await resolveAvailableSteps();

        if (availableSteps.length === 0) {
            return;
        }

        setSteps(availableSteps);
        setStepIndex(0);
        setPhase('active');
    }, [resolveAvailableSteps]);

    const skipTour = useCallback(() => {
        endTour('skipped');
    }, [endTour]);

    const finishTour = useCallback(() => {
        endTour('completed', true);
    }, [endTour]);

    const goNext = useCallback(() => {
        if (isLastStep) {
            finishTour();

            return;
        }

        setStepIndex((index) => index + 1);
    }, [finishTour, isLastStep]);

    const goBack = useCallback(() => {
        if (stepIndex === 0) {
            return;
        }

        setStepIndex((index) => index - 1);
    }, [stepIndex]);

    useEffect(() => {
        if (!tourCompletedForVersion) {
            const timeout = window.setTimeout(() => {
                setPhase('welcome');
            }, TOUR_WELCOME_DELAY_MS);

            return () => window.clearTimeout(timeout);
        }
    }, [tourCompletedForVersion]);

    useEffect(() => {
        if (!isTourActive || steps.length === 0) {
            return;
        }

        const step = steps[stepIndex];

        if (!step) {
            return;
        }

        let cancelled = false;

        const run = async () => {
            setIsPreparingStep(true);

            const isReady = await prepareStep(step);

            if (cancelled) {
                return;
            }

            setIsPreparingStep(false);

            if (!isReady) {
                if (stepIndex < steps.length - 1) {
                    setStepIndex((index) => index + 1);
                } else if (stepIndex > 0) {
                    setStepIndex((index) => index - 1);
                }
            }
        };

        void run();

        return () => {
            cancelled = true;
        };
    }, [isTourActive, prepareStep, stepIndex, steps]);

    useEffect(() => {
        if (!isTourActive) {
            return;
        }

        const handleKeyDown = (event: KeyboardEvent) => {
            if (event.key === 'Escape') {
                skipTour();

                return;
            }

            if (event.key === 'Enter' && event.shiftKey) {
                event.preventDefault();
                goBack();

                return;
            }

            if (event.key === 'Enter') {
                event.preventDefault();
                goNext();

                return;
            }

            if (event.key === 'ArrowRight' && !isLastStep) {
                event.preventDefault();
                goNext();
            }

            if (event.key === 'ArrowLeft' && stepIndex > 0) {
                event.preventDefault();
                goBack();
            }
        };

        document.addEventListener('keydown', handleKeyDown);

        return () => document.removeEventListener('keydown', handleKeyDown);
    }, [goBack, goNext, isLastStep, isTourActive, skipTour, stepIndex]);

    return {
        auth,
        currentStep,
        finishTour,
        goBack,
        goNext,
        isLastStep,
        isPreparingStep,
        isTourActive,
        phase,
        replayButtonRef,
        setPhase,
        showSkippedBadge,
        skipTour,
        startTour,
        stepIndex,
        steps,
        tourCompletedForVersion,
    };
}
