import type { AsukaElement, ElementVariation } from '../types/elements';
import { resolveBrowserImageGeometry } from './elementVariationRuntime';

export interface AsukaViewElementSource {
  id: string;
  name: string;
  url: string;
  element: AsukaElement;
  variation: ElementVariation | null;
  variationId?: string;
}

export interface AsukaViewElementGeometry extends AsukaViewElementSource {
  width: number;
  height: number;
}

const clean = (value: unknown): string => String(value ?? '').trim();

const firstNonEmpty = (...values: unknown[]): string => {
  for (const value of values) {
    const normalized = clean(value);
    if (normalized) return normalized;
  }
  return '';
};

const getVariationImage = (variation: ElementVariation | null | undefined): string => firstNonEmpty(
  variation?.image,
  variation?.icon,
);

const flattenVariations = (variations: ElementVariation[] | undefined): ElementVariation[] => {
  const result: ElementVariation[] = [];
  const visit = (items: ElementVariation[] | undefined) => {
    for (const variation of items ?? []) {
      if (!variation) continue;
      result.push(variation);
      visit(variation.variations);
    }
  };
  visit(variations);
  return result;
};

export const resolveViewElementSource = (element: AsukaElement | null | undefined): AsukaViewElementSource | null => {
  if (!element || clean(element.type).toLowerCase() === 'product-3d') return null;
  const id = clean(element.id ?? element.name);
  if (!id) return null;

  const variations = flattenVariations(element.variations);
  const activeVariation = variations.find((variation) => variation.active === true) ?? null;
  const firstVisualVariation = variations.find((variation) => Boolean(getVariationImage(variation))) ?? null;
  let variation = activeVariation ?? firstVisualVariation;
  let url = getVariationImage(variation) || firstNonEmpty(element.image, element.icon);

  if (!url && firstVisualVariation) {
    variation = firstVisualVariation;
    url = getVariationImage(firstVisualVariation);
  }
  if (!url) return null;

  return {
    id,
    name: clean(element.name) || 'Element',
    url,
    element,
    variation,
    ...(variation?.id ? { variationId: clean(variation.id) } : null),
  };
};

export const listViewElementSources = (elements: AsukaElement[] | null | undefined): AsukaViewElementSource[] => {
  const result: AsukaViewElementSource[] = [];
  const seen = new Set<string>();
  const visit = (items: AsukaElement[] | undefined) => {
    for (const element of items ?? []) {
      const source = resolveViewElementSource(element);
      if (source && !seen.has(source.id)) {
        seen.add(source.id);
        result.push(source);
      }
      visit(element.children);
    }
  };
  visit(elements ?? []);
  return result;
};

export const measureViewElementSource = async (
  source: AsukaViewElementSource,
  resolveGeometry: (url: string) => Promise<{ width: number; height: number } | null> = resolveBrowserImageGeometry,
): Promise<AsukaViewElementGeometry | null> => {
  const geometry = await resolveGeometry(source.url);
  const width = Math.round(Number(geometry?.width ?? 0));
  const height = Math.round(Number(geometry?.height ?? 0));
  if (!Number.isFinite(width) || !Number.isFinite(height) || width < 1 || height < 1) return null;
  return { ...source, width, height };
};
