/**
 * Constructor Helpers
 *
 * Utility functions for the constructor page.
 * Extracted from constructor.tsx for reusability.
 */

import type {
  CanvasElement,
  ConstructorAsset as ProjectAsset,
  AssetOption,
} from '../types/constructor';
import {
  isGalleryElementType,
  isCarouselElementType,
  isTooltipElementType,
  isDescriptionElementType,
  isNavigationElementType,
  isMediaElementType,
  getNavigationButtonLabel,
} from './elementDefaults';

/**
 * Clamp a number between min and max
 */
export const clamp = (value: number, min: number, max: number): number =>
  Math.min(Math.max(value, min), max);

/**
 * Get trimmed CSS value as string.
 * Handles null/undefined gracefully.
 */
export const getTrimmedCssValue = (value: unknown): string => {
  if (value === null || value === undefined) return '';
  return String(value).trim();
};

/**
 * Format asset label for display in select dropdowns.
 * Shows asset name with source path suffix.
 */
export const getAssetLabel = (asset: ProjectAsset): string => {
  const baseName = asset.name?.trim() || 'Untitled asset';
  const source = String(asset.storage_key || asset.cdn_url || '').trim();
  return `${baseName}${source ? ` · ${source}` : ''}`;
};

/**
 * Get asset source value (storage_key or cdn_url).
 * Used as the value in asset select dropdowns.
 */
export const getAssetSourceValue = (asset: ProjectAsset): string =>
  String(asset.storage_key || asset.cdn_url || '').trim();

/**
 * Check if an asset is likely a background image based on name/type.
 * Used to filter assets for background image selection.
 */
export const isBackgroundImageAsset = (asset: ProjectAsset): boolean => {
  if (asset.type) return asset.type === 'background_image';
  const normalizedName = String(asset.name || '').toLowerCase();
  if (!normalizedName) return false;
  const hasBackgroundKeyword = /\bbackground\b|\bbg\b|backdrop|wallpaper/.test(
    normalizedName,
  );
  const hasExcludedKeyword = /\bicon\b|\blogo\b/.test(normalizedName);
  return hasBackgroundKeyword && !hasExcludedKeyword;
};

/**
 * Add current value as a fallback option if not already present.
 * Ensures the currently selected value is always available in the dropdown.
 */
export const addFallbackAssetOption = (
  options: AssetOption[],
  value?: string,
  fallbackLabel?: string,
): AssetOption[] => {
  const normalizedValue = String(value || '').trim();
  if (!normalizedValue) return options;
  if (options.some((option) => option.value === normalizedValue))
    return options;
  return [
    ...options,
    {
      value: normalizedValue,
      label: fallbackLabel || `Custom URL · ${normalizedValue}`,
    },
  ];
};

/**
 * Get element button title for display in constructor menu.
 * Shows element-specific info like card/slide count.
 */
export const getElementButtonTitle = (element: CanvasElement): string => {
  if (isGalleryElementType(element.type)) {
    return `${element.label} (${element.galleryCards?.length || 0})`;
  }

  if (isCarouselElementType(element.type)) {
    return `${element.label} (${element.carouselSlides?.length || 0})`;
  }

  if (isTooltipElementType(element.type)) return element.tooltipTitle ?? '';
  if (isDescriptionElementType(element.type))
    return element.descriptionTitle ?? '';

  if (isNavigationElementType(element.type)) {
    type NavigationElementType = 'navigation_next' | 'navigation_prev';
    return (
      element.navLabel?.trim() ||
      getNavigationButtonLabel(element.type as NavigationElementType)
    );
  }

  if (isMediaElementType(element.type) && element.mediaUrl) {
    return `${element.label} · configured`;
  }

  return element.label;
};

/**
 * Build asset options for a specific asset type.
 * Filters assets by type and creates label/value pairs.
 */
export const buildAssetOptions = (
  assets: ProjectAsset[],
  assetType: 'image' | 'video' | 'audio',
  additionalFilter?: (asset: ProjectAsset) => boolean,
): AssetOption[] => {
  return assets
    .filter((asset) => {
      if (asset.asset_type !== assetType) return false;
      if (!getAssetSourceValue(asset)) return false;
      if (additionalFilter && !additionalFilter(asset)) return false;
      return true;
    })
    .map((asset) => ({
      value: getAssetSourceValue(asset),
      label: getAssetLabel(asset),
    }));
};

/**
 * Build background image asset options.
 * Filters for image assets that appear to be backgrounds.
 */
export const buildBackgroundImageOptions = (
  assets: ProjectAsset[],
): AssetOption[] => {
  return buildAssetOptions(assets, 'image', isBackgroundImageAsset);
};

/**
 * Build video asset options.
 */
export const buildVideoAssetOptions = (
  assets: ProjectAsset[],
): AssetOption[] => {
  return buildAssetOptions(assets, 'video');
};

/**
 * Build audio asset options.
 */
export const buildAudioAssetOptions = (
  assets: ProjectAsset[],
): AssetOption[] => {
  return buildAssetOptions(assets, 'audio');
};

/**
 * Build transition video asset options.
 * Prefers assets marked as type='transition', falls back to tagged or all videos.
 */
export const buildTransitionVideoOptions = (
  assets: ProjectAsset[],
): AssetOption[] => {
  // First try assets marked as transition type
  const typedAssets = assets
    .filter(
      (asset) =>
        asset.type === 'transition' &&
        asset.asset_type === 'video' &&
        getAssetSourceValue(asset),
    )
    .map((asset) => ({
      value: getAssetSourceValue(asset),
      label: getAssetLabel(asset),
    }));

  if (typedAssets.length > 0) return typedAssets;

  // Fall back to assets with [TRANSITION] tag in name
  const taggedAssets = assets
    .filter(
      (asset) =>
        asset.asset_type === 'video' &&
        getAssetSourceValue(asset) &&
        /\[TRANSITION\]/i.test(String(asset.name || '')),
    )
    .map((asset) => ({
      value: getAssetSourceValue(asset),
      label: getAssetLabel(asset),
    }));

  if (taggedAssets.length > 0) return taggedAssets;

  // Fall back to all video assets
  return buildVideoAssetOptions(assets);
};

/**
 * Build icon asset options.
 * Filters for image assets marked as type='icon'.
 */
export const buildIconAssetOptions = (
  assets: ProjectAsset[],
): AssetOption[] => {
  return assets
    .filter(
      (asset) =>
        asset.type === 'icon' &&
        asset.asset_type === 'image' &&
        getAssetSourceValue(asset),
    )
    .map((asset) => ({
      value: getAssetSourceValue(asset),
      label: getAssetLabel(asset),
    }));
};

/**
 * Build all image asset options (no filtering).
 */
export const buildImageAssetOptions = (
  assets: ProjectAsset[],
): AssetOption[] => {
  return buildAssetOptions(assets, 'image');
};

/**
 * Extract numeric value from CSS value string.
 * Used for compact style inputs that expect raw numbers.
 *
 * @example
 * extractNumericValue('24vw'); // '24'
 * extractNumericValue('100px'); // '100'
 * extractNumericValue(''); // ''
 */
export const extractNumericValue = (value?: string): string => {
  if (!value) return '';
  const match = String(value).match(/^(-?\d+\.?\d*)/);
  return match ? match[1] : '';
};

/**
 * Build page name lookup map by ID.
 */
export const buildPageNameById = (
  pages: Array<{ id: string; name?: string }>,
): Record<string, string> => {
  const acc: Record<string, string> = {};
  pages.forEach((page, index) => {
    acc[String(page.id)] = page.name || `Page ${index + 1}`;
  });
  return acc;
};

/**
 * Build page name lookup map by slug.
 */
export const buildPageNameBySlug = (
  pages: Array<{ slug?: string; name?: string }>,
): Record<string, string> => {
  const acc: Record<string, string> = {};
  pages.forEach((page, index) => {
    if (page.slug) {
      acc[String(page.slug)] = page.name || `Page ${index + 1}`;
    }
  });
  return acc;
};
