/**
 * 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 axios from 'axios';
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 LayoutGuest from '../layouts/Guest';
import { getPageTitle } from '../config';
import { usePreloadOrchestrator } from '../hooks/usePreloadOrchestrator';
import { extractPageLinksAndElements } from '../lib/extractPageLinks';
import { usePageSwitch } from '../hooks/usePageSwitch';
import { useTransitionPlayback } from '../hooks/useTransitionPlayback';
import { resolveAssetPlaybackUrl } from '../lib/assetUrl';
import { logger } from '../lib/logger';
import { buildElementStyle } from '../lib/elementStyles';
import type { RuntimeProject, RuntimePage } from '../types/runtime';

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

const getRows = (response: any) =>
  Array.isArray(response?.data?.rows) ? response.data.rows : [];

export default function RuntimePresentation({
  projectSlug,
  environment,
}: RuntimePresentationProps) {
  const [project, setProject] = useState<RuntimeProject | null>(null);
  const [pages, setPages] = useState<RuntimePage[]>([]);
  const [selectedPageId, setSelectedPageId] = useState<string | null>(null);
  const [pageHistory, setPageHistory] = useState<string[]>([]);
  const [transitionPreview, setTransitionPreview] = useState<{
    targetPageId: string;
    videoUrl: string;
    isReverse: boolean;
  } | null>(null);
  const [error, setError] = useState('');
  const [isLoading, setIsLoading] = useState(true);
  const [isFullscreen, setIsFullscreen] = useState(false);
  const [isBackgroundReady, setIsBackgroundReady] = useState(true);
  const [pendingTransitionComplete, setPendingTransitionComplete] =
    useState(false);
  const [isOverlayFadingOut, setIsOverlayFadingOut] = useState(false);

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

  // API request config with custom headers for project/environment
  const apiConfig = useMemo(
    () => ({
      headers: {
        'X-Runtime-Project-Slug': projectSlug,
        'X-Runtime-Environment': environment,
      },
    }),
    [projectSlug, environment],
  );

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

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

  // Fade out and remove transition overlay when background is ready
  useEffect(() => {
    if (pendingTransitionComplete && isBackgroundReady && !isOverlayFadingOut) {
      // Start fade-out animation
      setIsOverlayFadingOut(true);

      // After fade completes (300ms), remove the overlay
      const fadeTimer = setTimeout(() => {
        const video = transitionVideoRef.current;
        video?.removeAttribute('src');
        video?.load();
        setTransitionPreview(null);
        setPendingTransitionComplete(false);
        setIsOverlayFadingOut(false);
        // Clear previous background from shared hook
        pageSwitch.clearPreviousBackground();
      }, 300);

      return () => clearTimeout(fadeTimer);
    }
  }, [
    pendingTransitionComplete,
    isBackgroundReady,
    isOverlayFadingOut,
    pageSwitch.clearPreviousBackground,
  ]);

  // Clear previous background overlay when new background is ready (direct navigation)
  useEffect(() => {
    if (
      pageSwitch.isSwitching &&
      pageSwitch.isNewBgReady &&
      pageSwitch.previousBgImageUrl
    ) {
      // New background is ready - clear the previous background overlay
      pageSwitch.clearPreviousBackground();
    }
  }, [
    pageSwitch.isSwitching,
    pageSwitch.isNewBgReady,
    pageSwitch.previousBgImageUrl,
    pageSwitch.clearPreviousBackground,
  ]);

  // Load presentation data
  useEffect(() => {
    let isCancelled = false;

    const loadPresentation = async () => {
      try {
        setIsLoading(true);
        setError('');

        // Fetch project by slug
        const projectsResponse = await axios.get('/projects', {
          ...apiConfig,
          params: { slug: projectSlug },
        });

        if (isCancelled) return;

        const projectRows = getRows(projectsResponse);
        const foundProject = projectRows.find(
          (p: RuntimeProject) => p.slug === projectSlug,
        );

        if (!foundProject) {
          setError(`Project "${projectSlug}" not found.`);
          return;
        }

        setProject(foundProject);

        // Fetch pages for this project
        // (Elements and navigation are extracted from ui_schema_json)
        const pagesResponse = await axios.get('/tour_pages', {
          ...apiConfig,
          params: { project: foundProject.id },
        });

        if (isCancelled) return;

        const pageRows = getRows(pagesResponse);

        // Filter by environment and sort by sort_order
        // STRICT: Only show pages matching the exact environment
        // Production = production only, Stage = stage only, Dev = dev only
        const envFilteredPages = pageRows
          .filter((p: any) => p.environment === environment)
          .sort((a: any, b: any) => (a.sort_order ?? 0) - (b.sort_order ?? 0));

        setPages(envFilteredPages);

        // Set initial page (first page by sort_order)
        if (envFilteredPages.length > 0) {
          const firstPage = envFilteredPages[0];
          setSelectedPageId(firstPage.id);
          setPageHistory([firstPage.id]);
        }
      } catch (err: any) {
        if (isCancelled) return;
        const message =
          err?.response?.data?.message ||
          err?.message ||
          'Failed to load presentation.';
        setError(message);
      } finally {
        if (!isCancelled) {
          setIsLoading(false);
        }
      }
    };

    loadPresentation();

    return () => {
      isCancelled = true;
    };
  }, [projectSlug, environment, apiConfig]);

  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 when isOverlayFadingOut resets
        setIsOverlayFadingOut(false);
        setPendingTransitionComplete(false);
        // Play transition using useTransitionPlayback hook
        setTransitionPreview({
          targetPageId,
          videoUrl: resolveAssetPlaybackUrl(transitionVideoUrl),
          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],
  );

  const handleElementClick = useCallback(
    (element: any) => {
      // Disable navigation while transition is actively playing or buffering
      // Only block during active phases, not during fade-out (completed phase)
      const isActivelyPlaying =
        transitionPhase === 'preparing' ||
        transitionPhase === 'playing' ||
        transitionPhase === 'reversing';
      if (isActivelyPlaying || isBuffering) {
        return;
      }

      // Support both targetPageSlug (new) and targetPageId (legacy)
      const targetPageSlug = element.targetPageSlug;
      const legacyTargetPageId = element.targetPageId;

      // Resolve slug to page ID, or use legacy targetPageId
      let targetPageId: string | undefined;
      if (targetPageSlug) {
        const targetPage = pages.find((p) => p.slug === targetPageSlug);
        targetPageId = targetPage?.id;
      } else if (legacyTargetPageId) {
        targetPageId = legacyTargetPageId;
      }

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

      if (targetPageId) {
        const isBack =
          element.navType === 'back' || element.type === 'navigation_prev';
        // Get transition video URL from element itself
        const transitionVideoUrl = element.transitionVideoUrl;
        navigateToPage(targetPageId, transitionVideoUrl, isBack);
      }
    },
    [navigateToPage, pages, transitionPhase, isBuffering],
  );

  // Render element content based on type
  const renderElementContent = (element: any) => {
    // Navigation buttons
    if (
      element.type === 'navigation_next' ||
      element.type === 'navigation_prev'
    ) {
      if (element.iconUrl) {
        // Use img tag with flexible sizing - auto for dimensions not provided
        const imgStyle: React.CSSProperties = {
          width: element.width || 'auto',
          height: element.height || 'auto',
          objectFit: 'contain',
        };
        return (
          // eslint-disable-next-line @next/next/no-img-element
          <img
            src={resolveAssetPlaybackUrl(element.iconUrl)}
            alt='Navigation'
            style={imgStyle}
            draggable={false}
          />
        );
      }
      return (
        <div className='px-4 py-2 bg-white/80 rounded text-black text-sm'>
          {element.navLabel ||
            (element.type === 'navigation_next' ? 'Next' : 'Back')}
        </div>
      );
    }

    // Description element
    if (element.type === 'description') {
      if (element.iconUrl) {
        return (
          <div className='relative w-full h-full'>
            <Image
              src={resolveAssetPlaybackUrl(element.iconUrl)}
              alt='Description'
              fill
              className='object-contain'
              unoptimized
            />
          </div>
        );
      }
      const bgColor = element.descriptionBackgroundColor || 'transparent';
      return (
        <div className='p-4 rounded' style={{ backgroundColor: bgColor }}>
          <p
            className='font-bold'
            style={{
              fontSize: element.descriptionTitleFontSize || '24px',
              fontFamily: element.descriptionTitleFontFamily || 'inherit',
              color: element.descriptionTitleColor || '#ffffff',
            }}
          >
            {element.descriptionTitle || ''}
          </p>
          {element.descriptionText && (
            <p
              style={{
                fontSize: element.descriptionTextFontSize || '16px',
                fontFamily: element.descriptionTextFontFamily || 'inherit',
                color: element.descriptionTextColor || '#ffffff',
              }}
            >
              {element.descriptionText}
            </p>
          )}
        </div>
      );
    }

    // Tooltip
    if (element.type === 'tooltip') {
      if (element.iconUrl) {
        return (
          <div className='relative w-full h-full'>
            <Image
              src={resolveAssetPlaybackUrl(element.iconUrl)}
              alt='Tooltip'
              fill
              className='object-contain'
              unoptimized
            />
          </div>
        );
      }
      return (
        <div className='bg-white/90 p-3 rounded max-w-[200px]'>
          <p className='font-bold text-black text-sm'>{element.tooltipTitle}</p>
          <p className='text-gray-600 text-xs'>{element.tooltipText}</p>
        </div>
      );
    }

    // Video player
    if (element.type === 'video_player' && element.mediaUrl) {
      return (
        <video
          className='w-full h-full object-cover rounded'
          src={resolveAssetPlaybackUrl(element.mediaUrl)}
          controls
          autoPlay={Boolean(element.mediaAutoplay)}
          loop={Boolean(element.mediaLoop)}
          muted={Boolean(element.mediaMuted)}
          playsInline
        />
      );
    }

    // Audio player
    if (element.type === 'audio_player' && element.mediaUrl) {
      return (
        <audio
          className='w-full'
          src={resolveAssetPlaybackUrl(element.mediaUrl)}
          controls
          autoPlay={Boolean(element.mediaAutoplay)}
          loop={Boolean(element.mediaLoop)}
        />
      );
    }

    // Gallery
    if (element.type === 'gallery') {
      const cards = element.galleryCards || [];
      return (
        <div className='grid grid-cols-3 gap-2 p-2 bg-black/50 rounded'>
          {cards.map((card: any) => (
            <div key={card.id} className='relative aspect-square'>
              {card.imageUrl && (
                <Image
                  src={resolveAssetPlaybackUrl(card.imageUrl)}
                  alt={card.title || ''}
                  fill
                  className='object-cover rounded'
                  unoptimized
                />
              )}
            </div>
          ))}
        </div>
      );
    }

    // Carousel
    if (element.type === 'carousel') {
      const slides = element.carouselSlides || [];
      const firstSlide = slides[0];
      if (firstSlide?.imageUrl) {
        return (
          <div className='relative w-full h-full'>
            <Image
              src={resolveAssetPlaybackUrl(firstSlide.imageUrl)}
              alt={firstSlide.caption || ''}
              fill
              className='object-cover rounded'
              unoptimized
            />
          </div>
        );
      }
    }

    // Default: icon or image
    if (element.iconUrl) {
      return (
        <div className='relative w-full h-full'>
          <Image
            src={resolveAssetPlaybackUrl(element.iconUrl)}
            alt=''
            fill
            className='object-contain'
            unoptimized
          />
        </div>
      );
    }

    if (element.imageUrl) {
      return (
        <div className='relative w-full h-full'>
          <Image
            src={resolveAssetPlaybackUrl(element.imageUrl)}
            alt=''
            fill
            className='object-contain'
            unoptimized
          />
        </div>
      );
    }

    // Text fallback
    if (element.text) {
      return <div className='text-white'>{element.text}</div>;
    }

    return null;
  };

  // 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>{getPageTitle(project?.name || 'Presentation')}</title>
      </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,
            Image component enhances with optimized loading. bg-black prevents white flash. */}
        {backgroundImageUrl && !backgroundVideoUrl && (
          <div className='absolute inset-0 pointer-events-none'>
            <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) => {
            const xPercent = element.xPercent ?? 0;
            const yPercent = element.yPercent ?? 0;
            const rotation = element.rotation ?? 0;

            // Build element style using shared utility
            const elementStyle: React.CSSProperties = {
              left: `${xPercent}%`,
              top: `${yPercent}%`,
              transform: `translate(-50%, -50%)${rotation ? ` rotate(${rotation}deg)` : ''}`,
              ...buildElementStyle(element),
            };

            return (
              <div
                key={element.id}
                className='absolute cursor-pointer'
                style={elementStyle}
                onClick={() => handleElementClick(element)}
              >
                {renderElementContent(element)}
              </div>
            );
          })}
        </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>

        {/* Environment badge */}
        <div className='absolute top-4 left-4 z-50'>
          <span
            className={`px-2 py-1 rounded text-xs font-bold ${
              environment === 'stage'
                ? 'bg-yellow-500 text-black'
                : 'bg-green-500 text-white'
            }`}
          >
            {environment.toUpperCase()}
          </span>
        </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>
        )}
      </div>
    </>
  );
}

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