import {
    createContext,
    useCallback,
    useContext,
    useEffect,
    useMemo,
    useRef,
    useState,
    type CSSProperties,
    type ReactNode,
} from 'react';
import { isDesktopViewport, useSidePanelMachine } from '../panels/useSidePanelMachine';

const SIDEBAR_WIDTH_KEY = 'authSidebarWidth';
const SIDEBAR_COLLAPSED_KEY = 'authSidebarCollapsed';
const SIDEBAR_DEFAULT_WIDTH = 280;
const SIDEBAR_MIN_WIDTH = 220;
const SIDEBAR_MAX_WIDTH = 420;
const SIDEBAR_COLLAPSED_WIDTH = 56;

export type LeftMenuSizing = {
    defaultWidth?: number;
    minWidth?: number;
    maxWidth?: number;
    collapsedWidth?: number;
};

export type LeftMenuRenderState = {
    collapsed: boolean;
    mobileOpen: boolean;
    isDesktop: boolean;
    closeMenu: () => void;
};

export type LeftMenuContent = {
    ariaLabel?: string;
    render: (state: LeftMenuRenderState) => ReactNode;
};

export type LeftMenuStyle = CSSProperties & {
    '--auth-sidebar-width': string;
};

type LeftMenuContextValue = {
    collapsed: boolean;
    mobileOpen: boolean;
    content: LeftMenuContent;
    desktopMenuStyle: LeftMenuStyle;
    openMenu: (content?: LeftMenuContent) => void;
    closeMenu: () => void;
    toggleMenu: (content?: LeftMenuContent) => void;
    expandMenu: () => void;
    collapseMenu: () => void;
    toggleCollapsed: () => void;
    setMenuContent: (content: LeftMenuContent | null) => void;
    startResize: ReturnType<typeof useSidePanelMachine>['startResize'];
};

type LeftMenuProviderProps = {
    children: ReactNode;
    defaultContent: LeftMenuContent;
    closeOnPathname?: string;
    sizing?: LeftMenuSizing;
};

const LeftMenuContext = createContext<LeftMenuContextValue | undefined>(undefined);

function readSizingValue(value: number | undefined, fallback: number): number {
    if (!Number.isFinite(value) || value == null || value <= 0) {
        return fallback;
    }

    return value;
}

function resolveLeftMenuSizing(sizing: LeftMenuSizing | undefined) {
    const requestedMinWidth = readSizingValue(sizing?.minWidth, SIDEBAR_MIN_WIDTH);
    const requestedMaxWidth = readSizingValue(sizing?.maxWidth, SIDEBAR_MAX_WIDTH);
    const minWidth = Math.min(requestedMinWidth, requestedMaxWidth);
    const maxWidth = Math.max(requestedMinWidth, requestedMaxWidth);
    const requestedDefaultWidth = readSizingValue(sizing?.defaultWidth, SIDEBAR_DEFAULT_WIDTH);
    const defaultWidth = Math.min(maxWidth, Math.max(minWidth, requestedDefaultWidth));
    const requestedCollapsedWidth = readSizingValue(
        sizing?.collapsedWidth,
        SIDEBAR_COLLAPSED_WIDTH,
    );
    const collapsedWidth = Math.min(minWidth, requestedCollapsedWidth);

    return {
        defaultWidth,
        minWidth,
        maxWidth,
        collapsedWidth,
    };
}

function readStoredCollapsed(): boolean {
    if (!globalThis.window) {
        return false;
    }

    return localStorage.getItem(SIDEBAR_COLLAPSED_KEY) === '1';
}

export function LeftMenuProvider({
    children,
    defaultContent,
    closeOnPathname,
    sizing,
}: Readonly<LeftMenuProviderProps>) {
    const { defaultWidth, minWidth, maxWidth, collapsedWidth } = resolveLeftMenuSizing(sizing);
    const [collapsed, setCollapsed] = useState<boolean>(() => readStoredCollapsed());
    const [mobileOpen, setMobileOpen] = useState(false);
    const [content, setContent] = useState<LeftMenuContent>(defaultContent);
    const defaultContentRef = useRef(defaultContent);

    useEffect(() => {
        const previousDefaultContent = defaultContentRef.current;
        defaultContentRef.current = defaultContent;
        setContent((currentContent) => {
            if (currentContent === previousDefaultContent) {
                return defaultContent;
            }
            return currentContent;
        });
    }, [defaultContent]);

    useEffect(() => {
        if (!globalThis.window) {
            return;
        }

        localStorage.setItem(SIDEBAR_COLLAPSED_KEY, collapsed ? '1' : '0');
    }, [collapsed]);

    const expandMenu = useCallback(() => {
        setCollapsed(false);
    }, []);

    const collapseMenu = useCallback(() => {
        setCollapsed(true);
    }, []);

    const toggleCollapsed = useCallback(() => {
        setCollapsed((previous) => !previous);
    }, []);

    const closeMobile = useCallback(() => {
        setMobileOpen(false);
    }, []);

    const setMenuContent = useCallback((nextContent: LeftMenuContent | null) => {
        setContent(nextContent ?? defaultContentRef.current);
    }, []);

    const closeMenu = useCallback(() => {
        if (isDesktopViewport()) {
            collapseMenu();
            return;
        }

        closeMobile();
    }, [collapseMenu, closeMobile]);

    const openMenu = useCallback(
        (nextContent?: LeftMenuContent) => {
            if (nextContent) {
                setContent(nextContent);
            }

            if (isDesktopViewport()) {
                expandMenu();
                return;
            }

            setMobileOpen(true);
        },
        [expandMenu],
    );

    const toggleMenu = useCallback(
        (nextContent?: LeftMenuContent) => {
            if (nextContent) {
                setContent(nextContent);
            }

            if (isDesktopViewport()) {
                toggleCollapsed();
                return;
            }

            setMobileOpen((previous) => !previous);
        },
        [toggleCollapsed],
    );

    const handleCloseOnPathname = useCallback(() => {
        setMobileOpen(false);
        setContent(defaultContentRef.current);
    }, []);

    const { width, startResize } = useSidePanelMachine({
        storageKey: SIDEBAR_WIDTH_KEY,
        defaultWidth,
        minWidth,
        maxWidth,
        resizeAxis: 'from-left',
        resizingBodyClass: 'auth-sidebar-resizing',
        isOpen: mobileOpen,
        canResize: !collapsed,
        shouldPersistWidth: !collapsed,
        closeOnPathname,
        onCloseOnPathname: handleCloseOnPathname,
        onEscape: closeMobile,
    });

    const desktopMenuStyle = useMemo<LeftMenuStyle>(
        () => ({
            '--auth-sidebar-width': `${collapsed ? collapsedWidth : width}px`,
        }),
        [collapsed, collapsedWidth, width],
    );

    const value = useMemo<LeftMenuContextValue>(
        () => ({
            collapsed,
            mobileOpen,
            content,
            desktopMenuStyle,
            openMenu,
            closeMenu,
            toggleMenu,
            expandMenu,
            collapseMenu,
            toggleCollapsed,
            setMenuContent,
            startResize,
        }),
        [
            collapsed,
            mobileOpen,
            content,
            desktopMenuStyle,
            openMenu,
            closeMenu,
            toggleMenu,
            expandMenu,
            collapseMenu,
            toggleCollapsed,
            setMenuContent,
            startResize,
        ],
    );

    return <LeftMenuContext.Provider value={value}>{children}</LeftMenuContext.Provider>;
}

export function useLeftMenu() {
    const ctx = useContext(LeftMenuContext);
    if (!ctx) {
        throw new Error('useLeftMenu must be used within LeftMenuProvider');
    }
    return ctx;
}
