/**
 * 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 BaseButton from './BaseButton';
import CardBox from './CardBox';
import { OfflineToggle } from './Offline/OfflineToggle';
import RuntimeElement from './RuntimeElement';
import GalleryCarouselOverlay from './UiElements/GalleryCarouselOverlay';
import LayoutGuest from '../layouts/Guest';
import { usePreloadOrchestrator } from '../hooks/usePreloadOrchestrator';
import { usePageDataLoader } from '../hooks/usePageDataLoader';
import { useProjectAssets } from '../hooks/useProjectAssets';
import { extractPageLinksAndElements } from '../lib/extractPageLinks';
import { usePageSwitch } from '../hooks/usePageSwitch';
import { useTransitionPlayback } from '../hooks/useTransitionPlayback';
import { useBackgroundTransition } from '../hooks/useBackgroundTransition';
import { resolveAssetPlaybackUrl } from '../lib/assetUrl';
import { logger } from '../lib/logger';
import {
  resolveNavigationTarget,
  isTransitionBlocking,
} from '../lib/navigationHelpers';
import type { TransitionPhase } from '../types/presentation';

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);

  const [selectedPageId, setSelectedPageId] = useState<string | null>(null);
  const [pageHistory, setPageHistory] = useState<string[]>([]);
  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: any;
    initialIndex: number;
  } | null>(null);

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

  // Set initial page when data loads
  useEffect(() => {
    if (initialPageId && !selectedPageId) {
      setSelectedPageId(initialPageId);
      setPageHistory([initialPageId]);
    }
  }, [initialPageId, selectedPageId]);

  // Extract page links and preload elements from ui_schema_json
  // This enables the neighbor graph to find connected pages for preloading
  const { pageLinks, preloadElements } = useMemo(() => {
    const result = extractPageLinksAndElements(pages);
    if (result.pageLinks.length > 0 || result.preloadElements.length > 0) {
      logger.info('[PRELOAD] Extracted page links and elements', {
        pageLinksCount: result.pageLinks.length,
        preloadElementsCount: result.preloadElements.length,
        pageLinks: result.pageLinks.map((link) => ({
          from: link.from_pageId?.slice(-8),
          to: link.to_pageId?.slice(-8),
          hasTransition: !!link.transition?.video_url,
        })),
      });
    }
    return result;
  }, [pages]);

  // 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',
        }
      : null,
    onComplete: async (targetPageId) => {
      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, () => {
          setSelectedPageId(targetPageId);
          setPageHistory((prev) => [...prev, targetPageId]);
        });
        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, () => {
          setSelectedPageId(targetPageId);
          setPageHistory((prev) => [...prev, targetPageId]);
        });
      }
    },
    [pages, pageSwitch, resetFadeOut],
  );

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

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

      // 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),
      });

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

  // Handler for gallery card clicks
  const handleGalleryCardClick = useCallback(
    (element: any, 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;

  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-hidden bg-black'
        style={{
          backgroundImage: backgroundImageUrl
            ? `url("${backgroundImageUrl}")`
            : undefined,
          backgroundSize: 'cover',
          backgroundPosition: 'center',
        }}
      >
        {/* Background image element - 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 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 */}
        {backgroundVideoUrl && (
          <video
            key={backgroundVideoUrl}
            className='absolute inset-0 w-full h-full object-cover'
            src={backgroundVideoUrl}
            autoPlay
            loop
            muted
            playsInline
          />
        )}

        {/* Page elements */}
        <div className='absolute inset-0'>
          {pageElements.map((element: any) => (
            <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}
            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}
          />
        )}
      </div>
    </>
  );
}

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