/**
 * RuntimePresentation Component
 *
 * Renders a presentation for a specific project and environment.
 * Used by /p/[projectSlug] and /p/[projectSlug]/stage routes.
 */

import { mdiFullscreen, mdiFullscreenExit } from '@mdi/js';
import Head from 'next/head';
import Image from 'next/image';
import React, {
  ReactElement,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import BaseButton from './BaseButton';
import CardBox from './CardBox';
import { OfflineToggle } from './Offline/OfflineToggle';
import RuntimeElement from './RuntimeElement';
import GalleryCarouselOverlay from './UiElements/GalleryCarouselOverlay';
import { BackdropPortalProvider } from './BackdropPortal';
import LayoutGuest from '../layouts/Guest';
import { usePreloadOrchestrator } from '../hooks/usePreloadOrchestrator';
import { usePageDataLoader } from '../hooks/usePageDataLoader';
import { useProjectAssets } from '../hooks/useProjectAssets';
import { usePageNavigation } from '../hooks/usePageNavigation';
import {
  extractPageLinksOnly,
  extractElementsForPages,
} from '../lib/extractPageLinks';
import { usePageSwitch } from '../hooks/usePageSwitch';
import { useTransitionPlayback } from '../hooks/useTransitionPlayback';
import { useBackgroundTransition } from '../hooks/useBackgroundTransition';
import { useBackgroundVideoPlayback } from '../hooks/useBackgroundVideoPlayback';
import { resolveAssetPlaybackUrl } from '../lib/assetUrl';
import { logger } from '../lib/logger';
import {
  resolveNavigationTarget,
  isTransitionBlocking,
} from '../lib/navigationHelpers';
import type { TransitionPhase } from '../types/presentation';
import type { CanvasElement } from '../types/constructor';

interface RuntimePresentationProps {
  projectSlug: string;
  environment: 'stage' | 'production';
}

export default function RuntimePresentation({
  projectSlug,
  environment,
}: RuntimePresentationProps) {
  // Use shared hook for loading project and pages data
  const { project, pages, isLoading, error, initialPageId } = usePageDataLoader(
    {
      projectSlug,
      environment,
      apiHeaders: {
        'X-Runtime-Project-Slug': projectSlug,
        'X-Runtime-Environment': environment,
      },
    },
  );

  // Resolve project assets (favicon, og_image, logo) to presigned URLs
  const { faviconUrl, ogImageUrl } = useProjectAssets(project);

  // Page navigation with history tracking via shared hook
  const {
    currentPageId: selectedPageId,
    pageHistory,
    applyPageSelection,
    getNavigationContext,
  } = usePageNavigation({
    pages,
    defaultPageId: initialPageId || undefined,
    trackHistory: true,
  });

  const [transitionPreview, setTransitionPreview] = useState<{
    targetPageId: string;
    videoUrl: string;
    storageKey: string;
    isReverse: boolean;
  } | null>(null);
  const [isFullscreen, setIsFullscreen] = useState(false);
  const [isBackgroundReady, setIsBackgroundReady] = useState(true);
  const [pendingTransitionComplete, setPendingTransitionComplete] =
    useState(false);
  const [activeGalleryCarousel, setActiveGalleryCarousel] = useState<{
    element: CanvasElement;
    initialIndex: number;
  } | null>(null);

  const transitionVideoRef = useRef<HTMLVideoElement>(null);
  const lastInitializedPageIdRef = useRef<string | null>(null);

  // Note: Initial page selection is handled by usePageNavigation hook via defaultPageId

  // Phase 1: Extract pageLinks from ALL pages (needed for navigation graph)
  // This is lightweight - only extracts navigation structure, not asset URLs
  const pageLinks = useMemo(() => {
    const links = extractPageLinksOnly(pages);
    if (links.length > 0) {
      logger.info('[PRELOAD] Extracted page links', {
        count: links.length,
        links: links.map((link) => ({
          from: link.from_pageId?.slice(-8),
          to: link.to_pageId?.slice(-8),
          hasTransition: !!link.transition?.video_url,
        })),
      });
    }
    return links;
  }, [pages]);

  // Phase 2: Extract elements only for current + neighbor pages (progressive)
  // This avoids parsing ui_schema_json for all pages upfront
  const preloadElements = useMemo(() => {
    if (!selectedPageId || pages.length === 0) return [];

    // Build simple neighbor set from pageLinks
    const neighborIds = new Set<string>();
    neighborIds.add(selectedPageId); // Current page
    pageLinks.forEach((link) => {
      if (link.from_pageId === selectedPageId && link.to_pageId) {
        neighborIds.add(link.to_pageId); // Direct neighbors
      }
    });

    // Extract elements only for current + neighbors
    const elements = extractElementsForPages(pages, Array.from(neighborIds));
    logger.info('[PRELOAD] Extracted elements for pages', {
      currentPage: selectedPageId.slice(-8),
      pageCount: neighborIds.size,
      elementCount: elements.length,
    });
    return elements;
  }, [pages, pageLinks, selectedPageId]);

  // Initialize preload orchestrator with transformed data
  const preloadOrchestrator = usePreloadOrchestrator({
    pages,
    pageLinks,
    elements: preloadElements,
    currentPageId: selectedPageId,
    pageHistory,
    enabled: !isLoading && !error,
  });

  // Initialize page switch hook for smooth background transitions
  const pageSwitch = usePageSwitch({
    preloadCache: preloadOrchestrator
      ? {
          getReadyBlobUrl: preloadOrchestrator.getReadyBlobUrl,
          getCachedBlobUrl: preloadOrchestrator.getCachedBlobUrl,
          preloadedUrls: preloadOrchestrator.preloadedUrls,
        }
      : undefined,
  });

  // Integrate useTransitionPlayback hook for smooth transitions (matches Constructor pattern)
  const { isBuffering, phase: transitionPhase } = useTransitionPlayback({
    videoRef: transitionVideoRef,
    transition: transitionPreview
      ? {
          videoUrl: transitionPreview.videoUrl,
          storageKey: transitionPreview.storageKey,
          reverseMode: transitionPreview.isReverse ? 'reverse' : 'none',
          targetPageId: transitionPreview.targetPageId,
          displayName: 'Transition',
          isBack: transitionPreview.isReverse, // Pass through for history management
        }
      : null,
    onComplete: async (targetPageId, isBack) => {
      if (targetPageId) {
        const targetPage = pages.find((p) => p.id === targetPageId);
        // Mark this page as initialized to prevent redundant effect calls
        lastInitializedPageIdRef.current = targetPageId;
        // Use shared hook to resolve blob URLs and switch page
        await pageSwitch.switchToPage(targetPage, () => {
          // Use applyPageSelection for proper history management (pops on back)
          applyPageSelection(targetPageId, isBack ?? false);
        });
        setIsBackgroundReady(false);
        // Signal that transition is complete and waiting for Image onLoad
        setPendingTransitionComplete(true);
      } else {
        // No target page - clean up and remove overlay
        const video = transitionVideoRef.current;
        video?.removeAttribute('src');
        video?.load();
        setTransitionPreview(null);
        setPendingTransitionComplete(false);
      }
    },
    features: {
      useBlobUrl: true,
      // Don't pre-decode images in the hook - we handle it via overlay:
      // Overlay shows last transition frame while new page background loads behind it
      preDecodeImages: false,
    },
    preload: {
      preloadedUrls: preloadOrchestrator?.preloadedUrls || new Set(),
      getCachedBlobUrl: preloadOrchestrator?.getCachedBlobUrl,
      getReadyBlobUrl: preloadOrchestrator?.getReadyBlobUrl,
    },
  });

  // Use shared background transition hook for fade-out effects
  const { isOverlayFadingOut, resetFadeOut } = useBackgroundTransition({
    pageSwitch,
    fadeOut: {
      pendingTransitionComplete,
      isBackgroundReady,
      transitionVideoRef,
      onTransitionCleanup: useCallback(() => {
        setTransitionPreview(null);
        setPendingTransitionComplete(false);
      }, []),
    },
  });

  const toggleFullscreen = useCallback(async () => {
    try {
      if (!document.fullscreenElement) {
        await document.documentElement.requestFullscreen();
        setIsFullscreen(true);
      } else {
        await document.exitFullscreen();
        setIsFullscreen(false);
      }
    } catch (err) {
      logger.error(
        'Fullscreen toggle failed:',
        err instanceof Error ? err : { error: err },
      );
    }
  }, []);

  useEffect(() => {
    const handleFullscreenChange = () => {
      setIsFullscreen(Boolean(document.fullscreenElement));
    };

    document.addEventListener('fullscreenchange', handleFullscreenChange);
    return () =>
      document.removeEventListener('fullscreenchange', handleFullscreenChange);
  }, []);

  const selectedPage = useMemo(
    () => pages.find((p) => p.id === selectedPageId) || null,
    [pages, selectedPageId],
  );

  const pageElements = useMemo(() => {
    if (!selectedPage) return [];

    try {
      const uiSchema =
        typeof selectedPage.ui_schema_json === 'string'
          ? JSON.parse(selectedPage.ui_schema_json)
          : selectedPage.ui_schema_json;

      return Array.isArray(uiSchema?.elements) ? uiSchema.elements : [];
    } catch {
      return [];
    }
  }, [selectedPage]);

  // Set initial backgrounds when page first loads (before preload cache is populated)
  // The condition ensures this only runs once on initial load when backgrounds are empty.
  // After that, navigateToPage handles all subsequent navigation explicitly.
  useEffect(() => {
    if (selectedPage && lastInitializedPageIdRef.current !== selectedPage.id) {
      // Only initialize when backgrounds are empty (initial load)
      // navigateToPage handles subsequent navigation by calling switchToPage directly
      if (!pageSwitch.currentBgImageUrl && !pageSwitch.currentBgVideoUrl) {
        lastInitializedPageIdRef.current = selectedPage.id;
        pageSwitch.switchToPage(selectedPage);
      }
    }
  }, [
    selectedPage,
    pageSwitch.currentBgImageUrl,
    pageSwitch.currentBgVideoUrl,
    pageSwitch.switchToPage,
  ]);

  // Handle background ready state for pages without images or with videos
  useEffect(() => {
    // If no background image, or if there's a video (video takes over), mark as ready
    if (
      !selectedPage?.background_image_url ||
      selectedPage?.background_video_url
    ) {
      setIsBackgroundReady(true);
    }
  }, [selectedPage?.background_image_url, selectedPage?.background_video_url]);

  const navigateToPage = useCallback(
    async (
      targetPageId: string,
      transitionVideoUrl?: string,
      isBack = false,
    ) => {
      const targetPage = pages.find((p) => p.id === targetPageId);
      if (!targetPage) return;

      if (transitionVideoUrl) {
        // Reset states from previous transition before starting new one
        // This prevents the fade-out effect from re-triggering
        resetFadeOut();
        setPendingTransitionComplete(false);
        // Play transition using useTransitionPlayback hook
        setTransitionPreview({
          targetPageId,
          videoUrl: resolveAssetPlaybackUrl(transitionVideoUrl),
          storageKey: transitionVideoUrl, // Raw storage path for cache lookup
          isReverse: isBack,
        });
      } else {
        // Direct navigation - use shared hook for smooth transition
        // Previous background stays visible until new one is ready
        setIsBackgroundReady(false);
        // Mark this page as initialized to prevent redundant effect calls
        lastInitializedPageIdRef.current = targetPageId;

        await pageSwitch.switchToPage(targetPage, () => {
          // Use applyPageSelection for proper history management (pops on back)
          applyPageSelection(targetPageId, isBack);
        });
      }
    },
    [pages, pageSwitch, resetFadeOut, applyPageSelection],
  );

  const handleElementClick = useCallback(
    (element: CanvasElement) => {
      // Block navigation while transition is actively playing or buffering
      if (
        isTransitionBlocking(transitionPhase as TransitionPhase, isBuffering)
      ) {
        return;
      }

      // Get navigation context from hook for history-based back navigation
      const navContext = getNavigationContext();

      // Use shared helper to resolve navigation target with history context
      const navTarget = resolveNavigationTarget(element, pages, navContext);

      // Debug: log element navigation data
      logger.info('Element clicked', {
        elementType: element.type,
        targetPageSlug: element.targetPageSlug,
        legacyTargetPageId: element.targetPageId,
        resolvedTargetPageId: navTarget?.pageId,
        transitionVideoUrl: element.transitionVideoUrl,
        hasTransition: Boolean(element.transitionVideoUrl),
        navBackMode: element.navBackMode,
        previousPageId: navContext.previousPageId,
      });

      if (navTarget) {
        navigateToPage(
          navTarget.pageId,
          navTarget.transitionVideoUrl,
          navTarget.isBack,
        );
      }
    },
    [navigateToPage, pages, transitionPhase, isBuffering, getNavigationContext],
  );

  // Handler for gallery card clicks
  const handleGalleryCardClick = useCallback(
    (element: CanvasElement, cardIndex: number) => {
      if (element.galleryCards?.length > 0) {
        setActiveGalleryCarousel({ element, initialIndex: cardIndex });
      }
    },
    [],
  );

  // URL resolver that uses preloaded blob URLs when available (instant display)
  const resolveUrlWithBlob = useCallback(
    (url: string | undefined): string => {
      if (!url) return '';

      // Try to get blob URL from preload orchestrator (instant display)
      // Check storage key first (most reliable), then resolved URL
      const blobUrl =
        preloadOrchestrator?.getReadyBlobUrl(url) ||
        preloadOrchestrator?.getReadyBlobUrl(resolveAssetPlaybackUrl(url));
      if (blobUrl) return blobUrl;

      // Fall back to standard resolution
      return resolveAssetPlaybackUrl(url);
    },
    [preloadOrchestrator],
  );

  // Use resolved URLs from shared hook (blob URLs if cached, otherwise original URLs)
  // Blob URLs render instantly since data is local in memory
  const backgroundImageUrl = pageSwitch.currentBgImageUrl;
  const backgroundVideoUrl = pageSwitch.currentBgVideoUrl;

  // Background video playback settings from selected page
  const videoAutoplay = selectedPage?.background_video_autoplay ?? true;
  const videoLoop = selectedPage?.background_video_loop ?? true;
  const videoMuted = selectedPage?.background_video_muted ?? true;
  const videoStartTime =
    selectedPage?.background_video_start_time != null
      ? parseFloat(String(selectedPage.background_video_start_time))
      : null;
  const videoEndTime =
    selectedPage?.background_video_end_time != null
      ? parseFloat(String(selectedPage.background_video_end_time))
      : null;

  // Use background video playback hook for custom start/end time handling
  const { videoRef: bgVideoRef } = useBackgroundVideoPlayback({
    videoUrl: backgroundVideoUrl,
    autoplay: videoAutoplay,
    loop: videoLoop,
    muted: videoMuted,
    startTime: videoStartTime,
    endTime: videoEndTime,
  });

  // When endTime is set, we disable native loop and handle it via the hook
  const useNativeLoop = videoEndTime == null ? videoLoop : false;

  if (isLoading) {
    return (
      <div className='flex items-center justify-center min-h-screen bg-gray-900'>
        <div className='text-white text-xl'>Loading presentation...</div>
      </div>
    );
  }

  if (error) {
    return (
      <div className='flex items-center justify-center min-h-screen bg-gray-900'>
        <CardBox className='max-w-md'>
          <h2 className='text-xl font-bold text-red-500 mb-4'>Error</h2>
          <p className='text-gray-300'>{error}</p>
        </CardBox>
      </div>
    );
  }

  return (
    <>
      <Head>
        <title>{project?.name || 'Presentation'}</title>
        {faviconUrl && <link key='favicon' rel='icon' href={faviconUrl} />}
        {ogImageUrl && (
          <>
            <meta key='og:image' property='og:image' content={ogImageUrl} />
            <meta
              key='twitter:image:src'
              property='twitter:image:src'
              content={ogImageUrl}
            />
          </>
        )}
        {project?.name && (
          <>
            <meta key='og:title' property='og:title' content={project.name} />
            <meta
              key='twitter:title'
              property='twitter:title'
              content={project.name}
            />
          </>
        )}
        {project?.description && (
          <>
            <meta
              key='og:description'
              property='og:description'
              content={project.description}
            />
            <meta
              key='twitter:description'
              property='twitter:description'
              content={project.description}
            />
          </>
        )}
      </Head>

      <div
        className='relative w-screen h-screen overflow-clip bg-black'
        style={{
          backgroundImage: backgroundImageUrl
            ? `url("${backgroundImageUrl}")`
            : undefined,
          backgroundSize: 'cover',
          backgroundPosition: 'center',
        }}
      >
        <BackdropPortalProvider>
          {/* Background image element - z-1 keeps it below backdrop blur (z-5).
            CSS backgroundImage provides instant display.
            Use native img for blob URLs to prevent repeated fetch requests from Next.js Image. */}
          {backgroundImageUrl && !backgroundVideoUrl && (
            <div className='absolute inset-0 z-1 pointer-events-none'>
              {backgroundImageUrl.startsWith('blob:') ? (
                // eslint-disable-next-line @next/next/no-img-element
                <img
                  key={backgroundImageUrl}
                  src={backgroundImageUrl}
                  alt=''
                  className='absolute inset-0 w-full h-full object-cover'
                  onLoad={() => {
                    setIsBackgroundReady(true);
                    pageSwitch.markBackgroundReady();
                  }}
                  onError={() => {
                    setIsBackgroundReady(true);
                    pageSwitch.markBackgroundReady();
                  }}
                />
              ) : (
                <Image
                  key={backgroundImageUrl}
                  src={backgroundImageUrl}
                  alt=''
                  fill
                  sizes='100vw'
                  className='object-cover'
                  priority
                  unoptimized
                  onLoad={() => {
                    setIsBackgroundReady(true);
                    pageSwitch.markBackgroundReady();
                  }}
                  onError={() => {
                    setIsBackgroundReady(true);
                    pageSwitch.markBackgroundReady();
                  }}
                />
              )}
            </div>
          )}

          {/* Previous background overlay - shows during direct navigation until new bg is ready */}
          {pageSwitch.previousBgImageUrl &&
            pageSwitch.isSwitching &&
            !pageSwitch.isNewBgReady && (
              <div
                className='absolute inset-0 pointer-events-none z-10'
                style={{
                  backgroundImage: `url("${pageSwitch.previousBgImageUrl}")`,
                  backgroundSize: 'cover',
                  backgroundPosition: 'center',
                }}
              />
            )}

          {/* Background video - z-1 keeps it below backdrop blur (z-5) */}
          {backgroundVideoUrl && (
            <video
              ref={bgVideoRef}
              key={backgroundVideoUrl}
              className='absolute inset-0 z-1 h-full w-full object-cover'
              src={backgroundVideoUrl}
              autoPlay={videoAutoplay}
              loop={useNativeLoop}
              muted={videoMuted}
              playsInline
            />
          )}

          {/* Page elements - z-40 ensures they appear above carousel background (z-10) and carousel controls (z-30) */}
          <div className='absolute inset-0 z-40'>
            {pageElements.map((element: CanvasElement) => (
              <RuntimeElement
                key={element.id}
                element={element}
                onClick={() => handleElementClick(element)}
                resolveUrl={resolveUrlWithBlob}
                onGalleryCardClick={(cardIndex) =>
                  handleGalleryCardClick(element, cardIndex)
                }
              />
            ))}
          </div>

          {/* Controls: Offline toggle and Fullscreen button */}
          <div className='absolute top-4 right-4 z-50 flex items-center gap-2'>
            <OfflineToggle
              projectId={project?.id || null}
              projectSlug={projectSlug}
              projectName={project?.name}
              pages={pages}
              showLabel={false}
              size='small'
            />
            <BaseButton
              icon={isFullscreen ? mdiFullscreenExit : mdiFullscreen}
              color='info'
              small
              onClick={toggleFullscreen}
            />
          </div>

          {/* Transition overlay - uses useTransitionPlayback hook for smooth transitions */}
          {/* Opacity is 0 during 'preparing' phase to show old page while video loads */}
          {/* Also fades to 0 when isOverlayFadingOut to reveal the new page underneath */}
          {transitionPreview && (
            <div className='fixed inset-0 z-50 overflow-hidden pointer-events-none'>
              <video
                ref={transitionVideoRef}
                className='absolute inset-0 h-full w-full object-cover transition-opacity duration-300 ease-linear'
                style={{
                  opacity:
                    transitionPhase === 'preparing' ||
                    isBuffering ||
                    isOverlayFadingOut
                      ? 0
                      : 1,
                }}
                muted
                playsInline
                preload='auto'
                disablePictureInPicture
              />
            </div>
          )}

          {/* Gallery Carousel Overlay */}
          {activeGalleryCarousel && (
            <GalleryCarouselOverlay
              cards={activeGalleryCarousel.element.galleryCards || []}
              initialIndex={activeGalleryCarousel.initialIndex}
              onClose={() => setActiveGalleryCarousel(null)}
              resolveUrl={resolveUrlWithBlob}
              prevIconUrl={
                activeGalleryCarousel.element.galleryCarouselPrevIconUrl
              }
              nextIconUrl={
                activeGalleryCarousel.element.galleryCarouselNextIconUrl
              }
              backIconUrl={
                activeGalleryCarousel.element.galleryCarouselBackIconUrl
              }
              backLabel={
                activeGalleryCarousel.element.galleryCarouselBackLabel || 'BACK'
              }
              prevX={activeGalleryCarousel.element.galleryCarouselPrevX}
              prevY={activeGalleryCarousel.element.galleryCarouselPrevY}
              nextX={activeGalleryCarousel.element.galleryCarouselNextX}
              nextY={activeGalleryCarousel.element.galleryCarouselNextY}
              backX={activeGalleryCarousel.element.galleryCarouselBackX}
              backY={activeGalleryCarousel.element.galleryCarouselBackY}
              prevWidth={activeGalleryCarousel.element.galleryCarouselPrevWidth}
              prevHeight={
                activeGalleryCarousel.element.galleryCarouselPrevHeight
              }
              nextWidth={activeGalleryCarousel.element.galleryCarouselNextWidth}
              nextHeight={
                activeGalleryCarousel.element.galleryCarouselNextHeight
              }
              backWidth={activeGalleryCarousel.element.galleryCarouselBackWidth}
              backHeight={
                activeGalleryCarousel.element.galleryCarouselBackHeight
              }
              isEditMode={false}
            />
          )}
        </BackdropPortalProvider>

        {/* Toast notifications for offline download status */}
        <ToastContainer
          position='bottom-center'
          autoClose={5000}
          hideProgressBar={false}
          newestOnTop={false}
          closeOnClick
          rtl={false}
          pauseOnFocusLoss
          draggable
          pauseOnHover
          theme='dark'
        />
      </div>
    </>
  );
}

// Layout wrapper for standalone usage
RuntimePresentation.getLayout = function getLayout(page: ReactElement) {
  return <LayoutGuest>{page}</LayoutGuest>;
};
