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 '../components/BaseButton';
import CardBox from '../components/CardBox';
import LayoutGuest from '../layouts/Guest';
import { getPageTitle } from '../config';

type RuntimeMode = 'admin' | 'stage' | 'production' | 'unknown';

type RuntimeContext = {
  mode: RuntimeMode;
  projectSlug: string | null;
};

type RuntimeProject = {
  id: string;
  name?: string;
  slug?: string;
  description?: string;
  entry_page_slug?: string;
};

type RuntimePage = {
  id: string;
  slug?: string;
  name?: string;
  sort_order?: number;
  background_image_url?: string;
  background_video_url?: string;
  ui_schema_json?: string;
};

type RuntimeElement = {
  id: string;
  pageId?: string;
  name?: string;
  element_type?: string;
  sort_order?: number;
  is_visible?: boolean;
  x_percent?: number;
  y_percent?: number;
  width_percent?: number;
  height_percent?: number;
  rotation_deg?: number;
  style_json?: string;
  content_json?: string;
};

type RuntimePageLink = {
  id: string;
  from_pageId?: string;
  to_pageId?: string;
  transitionId?: string;
  direction?: string;
  external_url?: string;
  is_active?: boolean;
  trigger_selector?: string;
};

type RuntimeTransition = {
  id: string;
  name?: string;
  slug?: string;
  video_url?: string;
  duration_sec?: number;
  supports_reverse?: boolean;
};

type TransitionOverlayState = {
  targetPageId: string;
  transitionName: string;
  videoUrl: string;
  isReverse: boolean;
  durationSec?: number;
};

const getRows = (response: any) => (Array.isArray(response?.data?.rows) ? response.data.rows : []);
const parseJsonField = (value: unknown) => {
  if (value === null || value === undefined) return null;
  if (typeof value !== 'string') return value;
  if (!value.trim()) return null;

  try {
    return JSON.parse(value);
  } catch (error) {
    return value;
  }
};

const getElementPreviewText = (content: unknown) => {
  if (typeof content === 'string') return content;
  if (!content || typeof content !== 'object') return null;

  const candidateKeys = ['title', 'text', 'subtitle', 'description', 'body', 'label', 'value'];
  const contentRecord = content as Record<string, unknown>;
  const firstTextKey = candidateKeys.find((key) => typeof contentRecord[key] === 'string');

  return firstTextKey ? String(contentRecord[firstTextKey]) : null;
};

const RuntimePageView = () => {
  const [context, setContext] = useState<RuntimeContext | null>(null);
  const [projects, setProjects] = useState<RuntimeProject[]>([]);
  const [pages, setPages] = useState<RuntimePage[]>([]);
  const [elements, setElements] = useState<RuntimeElement[]>([]);
  const [pageLinks, setPageLinks] = useState<RuntimePageLink[]>([]);
  const [transitions, setTransitions] = useState<RuntimeTransition[]>([]);
  const [selectedPageId, setSelectedPageId] = useState<string | null>(null);
  const [pageHistory, setPageHistory] = useState<string[]>([]);
  const [overlayTransition, setOverlayTransition] = useState<TransitionOverlayState | null>(null);
  const [error, setError] = useState('');
  const [isLoading, setIsLoading] = useState(true);

  const overlayVideoRef = useRef<HTMLVideoElement | null>(null);
  const reverseAnimationFrame = useRef<number | null>(null);

  useEffect(() => {
    let isCancelled = false;

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

        const contextResponse = await axios.get('/runtime-context');
        if (isCancelled) return;
        setContext(contextResponse.data || { mode: 'unknown', projectSlug: null });

        const [projectsResponse, pagesResponse, elementsResponse, pageLinksResponse, transitionsResponse] = await Promise.all([
          axios.get('/projects'),
          axios.get('/tour_pages'),
          axios.get('/page_elements'),
          axios.get('/page_links'),
          axios.get('/transitions'),
        ]);

        if (isCancelled) return;

        setProjects(getRows(projectsResponse));
        setPages(getRows(pagesResponse));
        setElements(getRows(elementsResponse));
        setPageLinks(getRows(pageLinksResponse));
        setTransitions(getRows(transitionsResponse));
      } catch (runtimeError: any) {
        if (isCancelled) return;
        const message = runtimeError?.response?.data?.message || runtimeError?.message || 'Failed to load runtime data.';
        setError(message);
      } finally {
        if (!isCancelled) {
          setIsLoading(false);
        }
      }
    };

    loadRuntime();

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

  const currentProject = useMemo(() => projects[0] || null, [projects]);
  const defaultPage = useMemo(() => {
    if (!pages.length) return null;
    if (currentProject?.entry_page_slug) {
      const byEntrySlug = pages.find((page) => page.slug === currentProject.entry_page_slug);
      if (byEntrySlug) return byEntrySlug;
    }
    return [...pages].sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0))[0];
  }, [pages, currentProject]);
  const currentPage = useMemo(() => {
    if (!pages.length) return null;
    if (selectedPageId) {
      return pages.find((page) => page.id === selectedPageId) || null;
    }
    return defaultPage;
  }, [pages, selectedPageId, defaultPage]);

  const applyPageSelection = useCallback((targetPageId: string, isBack: boolean) => {
    setSelectedPageId(targetPageId);
    setPageHistory((prev) => {
      if (!prev.length) return [targetPageId];
      const currentId = prev[prev.length - 1];
      if (currentId === targetPageId) return prev;

      if (isBack && prev.length > 1 && prev[prev.length - 2] === targetPageId) {
        return prev.slice(0, -1);
      }

      return [...prev, targetPageId];
    });
  }, []);

  useEffect(() => {
    if (defaultPage?.id && !selectedPageId) {
      setSelectedPageId(defaultPage.id);
      setPageHistory((prev) => (prev.length ? prev : [defaultPage.id]));
    }
  }, [defaultPage, selectedPageId]);

  const pageElements = useMemo(() => {
    if (!currentPage) return [];
    return elements
      .filter((element) => element.pageId === currentPage.id)
      .sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0));
  }, [elements, currentPage]);

  const pageById = useMemo(() => {
    return pages.reduce((acc, page) => {
      acc[page.id] = page;
      return acc;
    }, {} as Record<string, RuntimePage>);
  }, [pages]);

  const transitionById = useMemo(() => {
    return transitions.reduce((acc, transition) => {
      acc[transition.id] = transition;
      return acc;
    }, {} as Record<string, RuntimeTransition>);
  }, [transitions]);

  const currentPageLinks = useMemo(() => {
    if (!currentPage) return [];
    return pageLinks.filter((link) => link.from_pageId === currentPage.id && link.is_active !== false);
  }, [pageLinks, currentPage]);

  const startNavigation = useCallback(
    ({ targetPageId, linkDirection, transition }: { targetPageId: string; linkDirection?: string; transition?: RuntimeTransition | null }) => {
      const currentId = selectedPageId || defaultPage?.id;
      if (!targetPageId || !currentId || targetPageId === currentId) {
        return;
      }

      const previousPageId = pageHistory.length > 1 ? pageHistory[pageHistory.length - 2] : null;
      const isBack = linkDirection === 'back' || previousPageId === targetPageId;
      const transitionName = transition?.name || transition?.slug || 'Transition';
      const canUseReverseVideo = isBack && transition?.supports_reverse !== false;

      if (!transition?.video_url) {
        applyPageSelection(targetPageId, isBack);
        return;
      }

      setOverlayTransition({
        targetPageId,
        transitionName,
        videoUrl: transition.video_url,
        isReverse: canUseReverseVideo,
        durationSec: typeof transition.duration_sec === 'number' ? transition.duration_sec : Number(transition.duration_sec),
      });
    },
    [applyPageSelection, defaultPage?.id, pageHistory, selectedPageId],
  );

  const finishOverlayTransition = useCallback(() => {
    if (reverseAnimationFrame.current !== null) {
      cancelAnimationFrame(reverseAnimationFrame.current);
      reverseAnimationFrame.current = null;
    }

    setOverlayTransition((active) => {
      if (!active) return null;
      applyPageSelection(active.targetPageId, active.isReverse);
      return null;
    });
  }, [applyPageSelection]);

  useEffect(() => {
    const video = overlayVideoRef.current;

    if (!overlayTransition || !video) {
      return;
    }

    const configuredDurationMs =
      (overlayTransition.durationSec && overlayTransition.durationSec > 0 ? overlayTransition.durationSec : 0.7) * 1000;
    let fallbackTimer: ReturnType<typeof setTimeout> | null = null;

    const cleanupReverseFrame = () => {
      if (reverseAnimationFrame.current !== null) {
        cancelAnimationFrame(reverseAnimationFrame.current);
        reverseAnimationFrame.current = null;
      }
    };

    const runReverse = () => {
      cleanupReverseFrame();
      const duration = Number.isFinite(video.duration) && video.duration > 0 ? video.duration : Math.max(configuredDurationMs / 1000, 0.7);
      const reverseSeconds = overlayTransition.durationSec && overlayTransition.durationSec > 0 ? overlayTransition.durationSec : duration;
      const reverseMs = Math.max(reverseSeconds * 1000, 400);
      const reverseRate = duration / reverseSeconds;
      const startTime = performance.now();
      video.pause();
      video.currentTime = duration;

      const step = (now: number) => {
        const elapsed = now - startTime;
        const nextTime = Math.max(duration - (elapsed / 1000) * reverseRate, 0);
        video.currentTime = nextTime;

        if (elapsed >= reverseMs || nextTime <= 0.001) {
          finishOverlayTransition();
          return;
        }

        reverseAnimationFrame.current = requestAnimationFrame(step);
      };

      reverseAnimationFrame.current = requestAnimationFrame(step);
    };

    const onLoadedMetadata = () => {
      if (overlayTransition.isReverse) {
        runReverse();
        return;
      }

      video.currentTime = 0;
      video.play().catch((playError) => {
        // eslint-disable-next-line no-console
        console.error('Transition video playback failed:', playError);
        fallbackTimer = setTimeout(finishOverlayTransition, configuredDurationMs);
      });
    };

    const onEnded = () => {
      finishOverlayTransition();
    };

    video.addEventListener('loadedmetadata', onLoadedMetadata);
    video.addEventListener('ended', onEnded);

    const fallbackMs = overlayTransition.isReverse
      ? Math.max((overlayTransition.durationSec && overlayTransition.durationSec > 0 ? overlayTransition.durationSec * 1000 : configuredDurationMs) + 300, 900)
      : configuredDurationMs + 300;
    fallbackTimer = setTimeout(finishOverlayTransition, fallbackMs);

    return () => {
      video.removeEventListener('loadedmetadata', onLoadedMetadata);
      video.removeEventListener('ended', onEnded);
      cleanupReverseFrame();
      if (fallbackTimer) clearTimeout(fallbackTimer);
    };
  }, [finishOverlayTransition, overlayTransition]);

  useEffect(() => {
    return () => {
      if (reverseAnimationFrame.current !== null) {
        cancelAnimationFrame(reverseAnimationFrame.current);
      }
    };
  }, []);

  return (
    <>
      <Head>
        <title>{getPageTitle('Runtime')}</title>
      </Head>
      <main className="min-h-screen p-4 md:p-8">
        <CardBox className="max-w-5xl mx-auto">
          <div className="space-y-4">
            <div className="flex justify-between items-center">
              <h1 className="text-xl font-semibold">Runtime Viewer</h1>
              <div className="text-sm text-gray-500">Mode: {context?.mode || 'unknown'}</div>
            </div>

            {isLoading && <p>Loading runtime data...</p>}

            {!isLoading && error && (
              <div className="space-y-3">
                <p className="text-red-600">{error}</p>
                <BaseButton href="/login" color="info" label="Sign In" />
              </div>
            )}

            {!isLoading && !error && (
              <div className="space-y-4">
                <div>
                  <h2 className="font-semibold">{currentProject?.name || 'Project'}</h2>
                  <p className="text-sm text-gray-500">{currentProject?.description || 'No description.'}</p>
                </div>

                {!!pages.length && (
                  <div className="border rounded p-3 bg-gray-50">
                    <p className="text-sm font-semibold mb-2">Pages</p>
                    <div className="flex gap-2 flex-wrap">
                      {pages
                        .slice()
                        .sort((a, b) => (a.sort_order || 0) - (b.sort_order || 0))
                        .map((page) => (
                          <BaseButton
                            key={page.id}
                            small
                            color={currentPage?.id === page.id ? 'info' : 'white'}
                            label={page.name || page.slug || 'Page'}
                            onClick={() => startNavigation({ targetPageId: page.id })}
                          />
                        ))}
                    </div>
                  </div>
                )}

                {currentPage && (
                  <div className="border rounded p-4 bg-white">
                    <h3 className="font-semibold mb-2">{currentPage.name || currentPage.slug || 'Page'}</h3>
                    {currentPage.background_image_url && (
                      <Image
                        src={currentPage.background_image_url}
                        alt={currentPage.name || 'Runtime page background'}
                        className="w-full max-h-80 object-cover rounded mb-3"
                        width={1280}
                        height={720}
                        unoptimized
                      />
                    )}
                    {currentPage.background_video_url && (
                      <p className="text-xs text-gray-500 mb-3">Background video: {currentPage.background_video_url}</p>
                    )}
                    <div className="space-y-2">
                      {pageElements.map((element) => (
                        <div key={element.id} className="border rounded px-3 py-2">
                          <div className="flex items-center justify-between gap-3">
                            <p className="font-medium">{element.name || element.element_type || 'Element'}</p>
                            <p className="text-xs text-gray-500">{element.is_visible === false ? 'Hidden' : 'Visible'}</p>
                          </div>
                          <p className="text-xs text-gray-500 mb-2">{element.element_type || 'unknown'}</p>

                          {(element.x_percent !== undefined || element.y_percent !== undefined) && (
                            <p className="text-xs text-gray-500 mb-2">
                              Position: {element.x_percent ?? 0}% / {element.y_percent ?? 0}% · Size: {element.width_percent ?? 0}% ×{' '}
                              {element.height_percent ?? 0}% · Rotation: {element.rotation_deg ?? 0}°
                            </p>
                          )}

                          {getElementPreviewText(parseJsonField(element.content_json)) && (
                            <p className="text-sm mb-2">{getElementPreviewText(parseJsonField(element.content_json))}</p>
                          )}

                          <details className="text-xs">
                            <summary className="cursor-pointer text-blue-600">Content JSON</summary>
                            <pre className="mt-2 p-2 rounded bg-gray-50 overflow-auto">
                              {JSON.stringify(parseJsonField(element.content_json), null, 2) || 'null'}
                            </pre>
                          </details>

                          <details className="text-xs mt-2">
                            <summary className="cursor-pointer text-blue-600">Style JSON</summary>
                            <pre className="mt-2 p-2 rounded bg-gray-50 overflow-auto">
                              {JSON.stringify(parseJsonField(element.style_json), null, 2) || 'null'}
                            </pre>
                          </details>
                        </div>
                      ))}
                      {!pageElements.length && <p className="text-sm text-gray-500">No page elements found.</p>}
                    </div>

                    <div className="mt-4 border-t pt-3">
                      <h4 className="font-semibold mb-2 text-sm">Page links / transitions</h4>
                      <div className="space-y-2">
                        {currentPageLinks.map((link) => {
                          const targetPage = link.to_pageId ? pageById[link.to_pageId] : null;
                          const transition = link.transitionId ? transitionById[link.transitionId] : null;

                          return (
                            <div key={link.id} className="border rounded p-2">
                              <div className="flex flex-wrap items-center gap-2 text-sm">
                                <span className="font-medium">{link.direction || 'link'}</span>
                                {targetPage && (
                                  <BaseButton
                                    small
                                    color="info"
                                    label={`Go to ${targetPage.name || targetPage.slug || 'page'}`}
                                    onClick={() =>
                                      startNavigation({
                                        targetPageId: targetPage.id,
                                        linkDirection: link.direction,
                                        transition,
                                      })
                                    }
                                  />
                                )}
                                {!targetPage && link.external_url && (
                                  <a className="text-blue-600 underline" href={link.external_url} target="_blank" rel="noreferrer">
                                    Open external
                                  </a>
                                )}
                              </div>
                              {transition && (
                                <p className="text-xs text-gray-500 mt-1">
                                  Transition: {transition.name || transition.slug || transition.id}
                                  {typeof transition.duration_sec === 'number' ? ` · ${transition.duration_sec}s` : ''}
                                  {transition.video_url ? ' · video' : ' · no video'}
                                </p>
                              )}
                              {link.trigger_selector && <p className="text-xs text-gray-500 mt-1">Trigger: {link.trigger_selector}</p>}
                            </div>
                          );
                        })}
                        {!currentPageLinks.length && <p className="text-sm text-gray-500">No active links from this page.</p>}
                      </div>
                    </div>
                  </div>
                )}

                {!currentPage && <p className="text-sm text-gray-500">No runtime page found for this host.</p>}
              </div>
            )}
          </div>
        </CardBox>
      </main>

      {overlayTransition && (
        <div className="fixed inset-0 z-50 bg-black/95 flex items-center justify-center">
          <video
            ref={overlayVideoRef}
            src={overlayTransition.videoUrl}
            className="w-full h-full object-cover"
            muted
            playsInline
            autoPlay={!overlayTransition.isReverse}
            preload="auto"
          />
          <div className="absolute bottom-4 left-4 text-xs text-white/80">
            {overlayTransition.transitionName} · {overlayTransition.isReverse ? 'back (reverse)' : 'forward'}
          </div>
        </div>
      )}
    </>
  );
};

RuntimePageView.getLayout = function getLayout(page: ReactElement) {
  return <LayoutGuest>{page}</LayoutGuest>;
};

export default RuntimePageView;
