import type { ComponentProps, ParentComponent } from 'solid-js';
import {
  type Component,
  For,
  type JSXElement,
  Show,
  mergeProps,
  splitProps,
} from 'solid-js';

import { cn } from '~/utils/styles';

export type TimelinePropsItem = Omit<
  TimelineItemProps,
  'isActive' | 'isActiveBullet' | 'bulletSize' | 'lineSize'
> & {
  bulletSize?: number;
};

export type TimelineProps = {
  items: TimelinePropsItem[];
  activeItem: number;
  bulletSize?: number;
  lineSize?: number;
};

/*
  No bullet or line is active when activeItem is -1
  First bullet is active only if activeItem is 0 or more
  First line is active only if activeItem is 1 or more
*/

const Timeline: Component<TimelineProps> = (rawProps) => {
  const props = mergeProps({ bulletSize: 16, lineSize: 2 }, rawProps);

  return (
    <ul
      style={{
        'padding-left': `${props.bulletSize / 2}px`,
      }}
    >
      <For each={props.items}>
        {(item, index) => (
          <TimelineItem
            title={item.title}
            description={item.description}
            bullet={item.bullet}
            isLast={index() === props.items.length - 1}
            isActive={
              props.activeItem === -1 ? false : props.activeItem >= index() + 1
            }
            isActiveBullet={
              props.activeItem === -1 ? false : props.activeItem >= index()
            }
            bulletSize={props.bulletSize}
            lineSize={props.lineSize}
          />
        )}
      </For>
    </ul>
  );
};

export type TimelineItemProps = {
  title: JSXElement;
  description?: JSXElement;
  bullet?: JSXElement;
  isLast?: boolean;
  isActive: boolean;
  isActiveBullet: boolean;
  class?: string;
  bulletSize: number;
  lineSize: number;
};

const TimelineItem: Component<TimelineItemProps> = (props) => {
  const [local, others] = splitProps(props, [
    'class',
    'bullet',
    'description',
    'title',
    'isLast',
    'isActive',
    'isActiveBullet',
    'bulletSize',
    'lineSize',
  ]);
  return (
    <li
      class={cn(
        'relative border-l pb-8 pl-8',
        local.isLast && 'border-l-transparent pb-0',
        local.isActive && !local.isLast && 'border-l-primary',
        local.class
      )}
      style={{
        'border-left-width': `${local.lineSize}px`,
      }}
      {...others}
    >
      <TimelineItemBullet
        lineSize={local.lineSize}
        bulletSize={local.bulletSize}
        isActive={local.isActiveBullet}
      >
        {local.bullet}
      </TimelineItemBullet>
      <TimelineItemTitle>{local.title}</TimelineItemTitle>
      <Show when={local.description}>
        <TimelineItemDescription>{local.description}</TimelineItemDescription>
      </Show>
    </li>
  );
};

export type TimelineItemBulletProps = {
  children?: JSXElement;
  isActive?: boolean;
  bulletSize: number;
  lineSize: number;
};

const TimelineItemBullet: Component<TimelineItemBulletProps> = (props) => {
  return (
    <div
      class={cn(
        `absolute top-0 flex items-center justify-center rounded-full border bg-background`,
        props.isActive && 'border-primary'
      )}
      style={{
        width: `${props.bulletSize}px`,
        height: `${props.bulletSize}px`,
        left: `${-props.bulletSize / 2 - props.lineSize / 2}px`,
        'border-width': `${props.lineSize}px`,
      }}
      aria-hidden="true"
    >
      {props.children}
    </div>
  );
};

const TimelineItemTitle: ParentComponent = (props) => {
  return (
    <div class="mb-1 font-semibold text-base leading-none">
      {props.children}
    </div>
  );
};

const TimelineItemDescription: Component<ComponentProps<'p'>> = (props) => {
  const [local, others] = splitProps(props, ['class', 'children']);
  return (
    <p class={cn('text-muted-foreground text-sm', local.class)} {...others}>
      {local.children}
    </p>
  );
};

export { Timeline };
