/**
 * Navigation Helpers
 *
 * Shared utilities for page navigation in RuntimePresentation and constructor.tsx.
 * Handles target page resolution, back navigation detection, and transition blocking.
 */

import type { RuntimePage } from '../types/runtime';
import type {
  NavigableElement,
  NavigationTarget,
  TransitionPhase,
} from '../types/presentation';
import { parseJsonObject } from './parseJson';

/**
 * Context for resolving history-based back navigation
 */
export interface NavigationContext {
  currentPageSlug?: string;
  previousPageId?: string | null;
}

/**
 * UI schema structure for type-safe parsing
 */
interface UiSchemaStructure {
  elements?: Array<Record<string, unknown>>;
}

/**
 * Find navigation element on sourcePage that points to targetPageSlug.
 * Used for history-based back navigation to get the forward transition.
 *
 * @param sourcePage - The page to search for navigation elements
 * @param targetPageSlug - The target page slug to find
 * @returns The navigation element pointing to target page, or null if not found
 */
export const findIncomingNavigationElement = (
  sourcePage: {
    ui_schema_json?: string | Record<string, unknown>;
    slug?: string;
  },
  targetPageSlug: string,
): NavigableElement | null => {
  // Parse ui_schema_json using shared utility
  const uiSchema = parseJsonObject<UiSchemaStructure>(
    sourcePage.ui_schema_json,
    {},
  );
  const elements = Array.isArray(uiSchema.elements) ? uiSchema.elements : [];

  // Find navigation element pointing to target page
  const found = elements.find(
    (el) =>
      isNavigationType(String(el.type || '')) &&
      el.targetPageSlug === targetPageSlug,
  );

  if (!found) return null;

  // Type assertion after validation
  return found as unknown as NavigableElement;
};

/**
 * Resolve history-based back navigation target.
 * Finds the previous page from history and looks up the forward transition that was used to arrive.
 *
 * @param pages - Available pages
 * @param currentPageSlug - Current page slug
 * @param previousPageId - Previous page ID from history
 * @returns Navigation target or null if previous page not found
 */
export const resolveHistoryBackTarget = (
  pages: RuntimePage[],
  currentPageSlug: string,
  previousPageId: string | null,
): NavigationTarget | null => {
  if (!previousPageId) return null;

  const previousPage = pages.find((p) => p.id === previousPageId);
  if (!previousPage) return null;

  // Look up the forward navigation element that brought user to current page
  const incomingElement = findIncomingNavigationElement(
    previousPage,
    currentPageSlug,
  );

  return {
    page: previousPage,
    pageId: previousPage.id,
    transitionVideoUrl: incomingElement?.transitionVideoUrl,
    transitionReverseMode: incomingElement?.transitionReverseMode,
    reverseVideoUrl: incomingElement?.reverseVideoUrl,
    isBack: true,
  };
};

/**
 * Resolve target page from element navigation properties.
 * Supports both targetPageSlug (new) and targetPageId (legacy).
 * Also supports history-based back navigation when navBackMode='history'.
 *
 * @param element - Element with navigation properties
 * @param pages - Available pages to search
 * @param context - Optional context for history-based navigation
 * @returns The target page or undefined if not found
 */
export const resolveNavigationTarget = (
  element: NavigableElement,
  pages: RuntimePage[],
  context?: NavigationContext,
): NavigationTarget | null => {
  // Handle history-based back navigation
  if (isBackNavigation(element) && element.navBackMode === 'history') {
    return resolveHistoryBackTarget(
      pages,
      context?.currentPageSlug || '',
      context?.previousPageId || null,
    );
  }

  // Standard target_page mode logic
  const targetPageSlug = element.targetPageSlug;
  const legacyTargetPageId = element.targetPageId;

  let targetPage: RuntimePage | undefined;

  if (targetPageSlug) {
    targetPage = pages.find((p) => p.slug === targetPageSlug);
  } else if (legacyTargetPageId) {
    targetPage = pages.find((p) => p.id === legacyTargetPageId);
  }

  if (!targetPage) {
    return null;
  }

  const isBack = isBackNavigation(element);

  return {
    page: targetPage,
    pageId: targetPage.id,
    transitionVideoUrl: element.transitionVideoUrl,
    transitionReverseMode: element.transitionReverseMode,
    reverseVideoUrl: element.reverseVideoUrl,
    isBack,
  };
};

/**
 * Determine if navigation direction is "back".
 * Elements with navType='back' or type='navigation_prev' navigate backwards.
 *
 * @param element - Element to check
 * @returns true if this is a back navigation
 */
export const isBackNavigation = (element: NavigableElement): boolean => {
  return element.navType === 'back' || element.type === 'navigation_prev';
};

/**
 * Get navigation direction based on element properties.
 *
 * @param element - Element with navigation properties
 * @returns 'back' or 'forward'
 */
export const getNavigationDirection = (
  element: NavigableElement,
): 'back' | 'forward' => {
  return isBackNavigation(element) ? 'back' : 'forward';
};

/**
 * Check if transition is actively blocking navigation.
 * Navigation should be blocked during preparing, playing, or reversing phases.
 *
 * @param transitionPhase - Current transition phase
 * @param isBuffering - Whether video is buffering
 * @returns true if navigation should be blocked
 */
export const isTransitionBlocking = (
  transitionPhase: TransitionPhase,
  isBuffering: boolean,
): boolean => {
  const activePhases: TransitionPhase[] = ['preparing', 'playing', 'reversing'];
  return activePhases.includes(transitionPhase) || isBuffering;
};

/**
 * Check if element has a playable transition.
 * A transition is playable if it has a video URL, and for back navigation,
 * either supports reverse or has a separate reverse video.
 *
 * @param element - Element with transition properties
 * @param direction - Navigation direction
 * @returns true if element has a playable transition
 */
export const hasPlayableTransition = (
  element: {
    transitionVideoUrl?: string;
    transitionReverseMode?: string;
    reverseVideoUrl?: string;
  },
  direction: 'back' | 'forward' = 'forward',
): boolean => {
  if (!element.transitionVideoUrl) {
    return false;
  }

  // For back navigation with separate_video mode, need reverse video
  if (
    direction === 'back' &&
    element.transitionReverseMode === 'separate_video' &&
    !element.reverseVideoUrl
  ) {
    return false;
  }

  return true;
};

/**
 * Check if element is a navigation element type.
 *
 * @param elementType - Element type to check
 * @returns true if element is a navigation type
 */
export const isNavigationType = (elementType: string): boolean => {
  return (
    elementType === 'navigation_next' ||
    elementType === 'navigation_prev' ||
    elementType === 'navigation'
  );
};
