import { animateTo, shimKeyframesHeightAuto, stopAnimations } from '../internal/animate';
import { getAnimation, setDefaultAnimation } from '../utilities/animation-registry';
import { waitForEvent } from '../internal/event';
import type { NileDetail } from './nile-detail';

/*
  Runs the show animation on the detail body.
  Emits nile-show (cancelable) before and nile-after-show after the animation.
*/
export async function animateShow(component: NileDetail): Promise<void> {
  const nileShow = component.emit('nile-show', { cancelable: true });
  if (nileShow.defaultPrevented) {
    component.open = false;
    return;
  }

  await stopAnimations(component.body);
  component.body.hidden = false;

  const { keyframes, options } = getAnimation(component, 'detail.show', { dir: 'ltr' });
  await animateTo(component.body, shimKeyframesHeightAuto(keyframes, component.body.scrollHeight), options);
  component.body.style.height = 'auto';

  component.emit('nile-after-show');
}

/*
  Runs the hide animation on the detail body.
  Emits nile-hide (cancelable) before and nile-after-hide after the animation.
*/
export async function animateHide(component: NileDetail): Promise<void> {
  const nileHide = component.emit('nile-hide', { cancelable: true });
  if (nileHide.defaultPrevented) {
    component.open = true;
    return;
  }

  await stopAnimations(component.body);

  const { keyframes, options } = getAnimation(component, 'detail.hide', { dir: 'ltr' });
  await animateTo(component.body, shimKeyframesHeightAuto(keyframes, component.body.scrollHeight), options);
  component.body.hidden = true;
  component.body.style.height = 'auto';

  component.emit('nile-after-hide');
}

/*
  Programmatically shows the detail if it is not already open or disabled.
  Returns a promise that resolves after the show animation completes.
*/
export async function showDetail(component: NileDetail): Promise<unknown> {
  if (component.open || component.disabled) {
    return undefined;
  }
  component.open = true;
  return waitForEvent(component, 'nile-after-show');
}

/*
  Programmatically hides the detail if it is not already closed or disabled.
  Returns a promise that resolves after the hide animation completes.
*/
export async function hideDetail(component: NileDetail): Promise<unknown> {
  if (!component.open || component.disabled) {
    return undefined;
  }
  component.open = false;
  return waitForEvent(component, 'nile-after-hide');
}

/*
  Handles click on the header summary row.
  Toggles the detail open/close and focuses the header.
*/
export function handleSummaryClick(component: NileDetail): void {
  if (!component.disabled) {
    if (component.open) {
      hideDetail(component);
    } else {
      showDetail(component);
    }
  }
}

/*
  Handles keyboard interaction on the header summary row.
  Enter/Space toggles, ArrowUp/Left closes, ArrowDown/Right opens.
*/
export function handleSummaryKeyDown(event: KeyboardEvent, component: NileDetail): void {
  event.preventDefault();

  if (event.key === 'Enter' || event.key === ' ') {
    if (component.open) {
      hideDetail(component);
    } else {
      showDetail(component);
    }
  }

  if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {
    hideDetail(component);
  }

  if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {
    showDetail(component);
  }
}

/*
  Registers the default show and hide animations for the detail component.
  Show: animates from height 0 / opacity 0 to auto / 1 over 300ms.
  Hide: animates from auto / 1 to height 0 / opacity 0 over 300ms.
*/
export function registerDefaultAnimations(): void {
  setDefaultAnimation('detail.show', {
    keyframes: [
      { height: '0', opacity: '0' },
      { height: 'auto', opacity: '1' }
    ],
    options: { duration: 300, easing: 'ease' }
  });

  setDefaultAnimation('detail.hide', {
    keyframes: [
      { height: 'auto', opacity: '1' },
      { height: '0', opacity: '0' }
    ],
    options: { duration: 300, easing: 'ease' }
  });
}

registerDefaultAnimations();