export interface StackingContextDescriptor {
  parentCompositionId: string | null;
  compositionAncestors: readonly string[];
  stackingContextId?: string | null;
}

export interface ContextOrderItem extends StackingContextDescriptor {
  zIndex: number;
}

// fallow-ignore-next-line complexity
export function getElementZIndex(element: HTMLElement): number {
  try {
    const inline = element.style?.zIndex;
    if (inline && inline !== "auto") {
      const parsed = parseInt(inline, 10);
      if (Number.isFinite(parsed)) return parsed;
    }
    const win = element.ownerDocument?.defaultView;
    if (!win) return 0;
    const value = win.getComputedStyle(element).zIndex;
    if (value === "auto" || value === "") return 0;
    const parsed = parseInt(value, 10);
    return Number.isFinite(parsed) ? parsed : 0;
  } catch {
    return 0;
  }
}

// fallow-ignore-next-line complexity
export function hasExplicitZIndex(element: HTMLElement): boolean {
  try {
    const inline = element.style?.zIndex;
    if (inline) return inline !== "auto";
    const win = element.ownerDocument?.defaultView;
    if (!win) return false;
    const value = win.getComputedStyle(element).zIndex;
    return value !== "auto" && value !== "";
  } catch {
    return false;
  }
}

export function computeReorderZValues(
  existingValues: readonly number[],
  fromIndex: number,
  toIndex: number,
): number[] {
  const reordered = [...existingValues];
  const [moved] = reordered.splice(fromIndex, 1);
  reordered.splice(toIndex, 0, moved);

  const sorted = [...existingValues].sort((a, b) => b - a);
  const hasDupes = sorted.some((v, i) => i > 0 && v === sorted[i - 1]);
  return hasDupes ? reordered.map((_, i) => reordered.length - i) : sorted;
}

export function resolveStackingContextKey(item: StackingContextDescriptor): string {
  return item.stackingContextId ?? item.parentCompositionId ?? item.compositionAncestors[0] ?? "";
}

function resolveStackingContextDepth(item: StackingContextDescriptor): number {
  const contextKey = resolveStackingContextKey(item);
  if (!contextKey) return 0;
  const index = item.compositionAncestors.indexOf(contextKey);
  return index >= 0 ? index : 0;
}

export function resolveContextOrder<T extends ContextOrderItem>(items: readonly T[]): T[] {
  if (items.length === 0) return [];

  const groups = new Map<
    string,
    { firstIndex: number; depth: number; entries: Array<{ item: T; index: number }> }
  >();

  items.forEach((item, index) => {
    const key = resolveStackingContextKey(item);
    const group = groups.get(key);
    if (group) {
      group.entries.push({ item, index });
      return;
    }
    groups.set(key, {
      firstIndex: index,
      depth: resolveStackingContextDepth(item),
      entries: [{ item, index }],
    });
  });

  return [...groups.values()]
    .sort((a, b) => a.depth - b.depth || a.firstIndex - b.firstIndex)
    .flatMap((group) =>
      group.entries
        .sort((a, b) => b.item.zIndex - a.item.zIndex || a.index - b.index)
        .map((entry) => entry.item),
    );
}
