import { useEffect, useRef, useMemo, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import {
    Tldraw,
    Editor,
    useTldrawUser,
    DEFAULT_SUPPORTED_IMAGE_TYPES,
    DEFAULT_SUPPORT_VIDEO_TYPES,
} from '@tldraw/tldraw';
import { useSync } from '@tldraw/sync';
// App context
import { useAuth } from '../../contexts/AuthContext';
import { useTLDraw } from '../../contexts/TLDrawContext';
import { useNeoInstitute } from '../../contexts/NeoInstituteContext';
// Tldraw services
import { multiplayerOptions } from '../../services/tldraw/optionsService';
import { PresentationService } from '../../services/tldraw/presentationService';
import { createSyncConnectionOptions, handleExternalAsset } from '../../services/tldraw/syncService';
// Tldraw utils
import { getUiOverrides, getUiComponents } from '../../utils/tldraw/ui-overrides';
import { customAssets } from '../../utils/tldraw/assets';
import { multiplayerTools } from '../../utils/tldraw/tools';
import { allShapeUtils } from '../../utils/tldraw/shapes';
import { customSchema } from '../../utils/tldraw/schemas';
import { allBindingUtils } from '../../utils/tldraw/bindings';
import { multiplayerEmbeds } from '../../utils/tldraw/embeds';
// Layout
import { HEADER_HEIGHT } from '../../pages/Layout';
import { TLSYNC_URL } from '../../config/apiConfig';
// Styles
import '../../utils/tldraw/tldraw.css';
// App debug
import { logger } from '../../debugConfig';

const SYNC_WORKER_URL = TLSYNC_URL;

const apiBase = (import.meta.env.VITE_API_BASE as string) || '';

/**
 * Fetches a short-lived TLSync token from the API using the current Supabase session.
 * Returns { token, error } — exactly one will be non-null.
 */
function useTlsyncToken(accessToken: string | null) {
    const [token, setToken] = useState<string | null>(null);
    const [error, setError] = useState<string | null>(null);

    useEffect(() => {
        if (!accessToken) return;
        let cancelled = false;

        logger.debug('multiplayer-page', '🔑 Fetching TLSync token from API');
        fetch(`${apiBase}/api/tlsync/token`, {
            headers: { Authorization: `Bearer ${accessToken}` },
        })
            .then((r) => {
                if (!r.ok) throw new Error(`Token request failed: ${r.status}`);
                return r.json();
            })
            .then((data) => {
                if (!cancelled) {
                    logger.debug('multiplayer-page', '✅ TLSync token received', { expiresIn: data.expires_in });
                    setToken(data.token);
                }
            })
            .catch((err) => {
                if (!cancelled) {
                    logger.error('multiplayer-page', '❌ Failed to fetch TLSync token', { err: err?.message });
                    setError(err?.message || 'Unknown error');
                }
            });

        return () => {
            cancelled = true;
        };
    }, [accessToken]);

    return { token, error };
}

/**
 * Loading / error overlay shown while the TLSync token is being fetched.
 */
function TlsyncStatusOverlay({ message }: { message: string }) {
    return (
        <div
            style={{
                position: 'fixed',
                inset: 0,
                top: `${HEADER_HEIGHT}px`,
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'center',
                backgroundColor: 'rgba(0, 0, 0, 0.1)',
            }}
        >
            <div
                style={{
                    padding: '20px',
                    backgroundColor: 'white',
                    borderRadius: '8px',
                    boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
                }}
            >
                {message}
            </div>
        </div>
    );
}

/**
 * Inner component that owns the useSync hook.
 * Rendered only once a valid TLSync token has been fetched from the API.
 */
function TldrawCanvas({
    tlsyncToken,
    roomId,
}: {
    tlsyncToken: string;
    roomId: string;
}) {
    const { user } = useAuth();
    const {
        tldrawPreferences,
        setTldrawPreferences,
        initializePreferences,
        presentationMode,
    } = useTLDraw();
    const { isLoading: isInstituteLoading, isInitialized: isInstituteInitialized } = useNeoInstitute();
    const editorRef = useRef<Editor | null>(null);

    const userInfo = useMemo(
        () => ({
            id: user?.id ?? '',
            name: user?.display_name ?? user?.email?.split('@')[0] ?? 'Anonymous User',
            color: tldrawPreferences?.color ?? `hsl(${Math.random() * 360}, 70%, 50%)`,
        }),
        [user?.id, user?.display_name, user?.email, tldrawPreferences?.color],
    );

    const editorUser = useTldrawUser({
        userPreferences: {
            id: userInfo.id,
            name: userInfo.name,
            color: userInfo.color,
            locale: tldrawPreferences?.locale,
            colorScheme: tldrawPreferences?.colorScheme,
            animationSpeed: tldrawPreferences?.animationSpeed,
            isSnapMode: tldrawPreferences?.isSnapMode,
        },
        setUserPreferences: setTldrawPreferences,
    });

    const connectionOptions = useMemo(
        () =>
            createSyncConnectionOptions({
                userId: userInfo.id,
                displayName: userInfo.name,
                color: userInfo.color,
                roomId,
                baseUrl: SYNC_WORKER_URL,
                token: tlsyncToken,
            }),
        [userInfo, roomId, tlsyncToken],
    );

    const store = useSync({
        ...connectionOptions,
        schema: customSchema,
        shapeUtils: allShapeUtils,
        bindingUtils: allBindingUtils,
        userInfo: {
            id: userInfo.id,
            name: userInfo.name,
            color: userInfo.color,
        },
    });

    useEffect(() => {
        logger.info('multiplayer-page', `🔄 Connection status changed: ${store.status}`, {
            status: store.status,
            roomId: connectionOptions.roomId,
        });
    }, [store.status, connectionOptions.roomId]);

    useEffect(() => {
        if (user?.id && !tldrawPreferences) {
            logger.info('multiplayer-page', '🔄 Initializing preferences');
            initializePreferences(user.id);
        }
    }, [user?.id, tldrawPreferences, initializePreferences]);

    useEffect(() => {
        if (presentationMode && editorRef.current) {
            const editor = editorRef.current;
            const presentationService = new PresentationService(editor);
            const cleanup = presentationService.startPresentationMode();
            return () => {
                presentationService.stopPresentationMode();
                cleanup();
            };
        }
    }, [presentationMode]);

    const uiOverrides = useMemo(() => getUiOverrides(presentationMode), [presentationMode]);
    const uiComponents = useMemo(() => getUiComponents(presentationMode), [presentationMode]);

    if (store.status !== 'synced-remote' || isInstituteLoading || !isInstituteInitialized) {
        return <TlsyncStatusOverlay message={`Connecting to room: ${roomId}...`} />;
    }

    return (
        <div
            style={{
                position: 'fixed',
                inset: 0,
                top: `${HEADER_HEIGHT}px`,
                display: 'flex',
                flexDirection: 'column',
                overflow: 'hidden',
            }}
        >
            <Tldraw
                user={editorUser}
                store={store.store}
                onMount={(editor) => {
                    editorRef.current = editor;
                    editor.registerExternalAssetHandler('url', async ({ url }: { url: string }) => {
                        return handleExternalAsset(SYNC_WORKER_URL, url);
                    });
                }}
                options={multiplayerOptions}
                embeds={multiplayerEmbeds}
                tools={multiplayerTools}
                shapeUtils={allShapeUtils}
                bindingUtils={allBindingUtils}
                overrides={uiOverrides}
                components={uiComponents}
                assetUrls={customAssets}
                autoFocus={true}
                hideUi={false}
                acceptedImageMimeTypes={DEFAULT_SUPPORTED_IMAGE_TYPES}
                acceptedVideoMimeTypes={DEFAULT_SUPPORT_VIDEO_TYPES}
                maxImageDimension={Infinity}
                maxAssetSize={100 * 1024 * 1024}
                renderDebugMenuItems={() => []}
            />
        </div>
    );
}

export default function TldrawMultiUser() {
    const { user, accessToken } = useAuth();
    const navigate = useNavigate();
    const [searchParams] = useSearchParams();
    const roomId = searchParams.get('room') || 'multiplayer';

    const { token: tlsyncToken, error: tlsyncError } = useTlsyncToken(accessToken);

    // Redirect unauthenticated users
    useEffect(() => {
        if (!user) {
            navigate('/');
        }
    }, [user, navigate]);

    if (!user) {
        return null;
    }

    if (tlsyncError) {
        return <TlsyncStatusOverlay message={`Failed to connect to collaboration server: ${tlsyncError}`} />;
    }

    if (!tlsyncToken) {
        return <TlsyncStatusOverlay message="Connecting to collaboration server..." />;
    }

    return <TldrawCanvas tlsyncToken={tlsyncToken} roomId={roomId} />;
}
