<script setup lang="ts">
import type { Ref } from 'vue';
import {
  defaultDocument,
  useEventListener,
  useMediaQuery,
  useVModel,
} from '@vueuse/core';
import { TooltipProvider } from 'reka-ui';
import { computed, ref } from 'vue';
import { cn } from '@/lib/utils';
import {
  provideSidebarContext,
  SIDEBAR_COOKIE_MAX_AGE,
  SIDEBAR_COOKIE_NAME,
  SIDEBAR_KEYBOARD_SHORTCUT,
  SIDEBAR_WIDTH,
  SIDEBAR_WIDTH_ICON,
} from './utils';

defineOptions({ inheritAttrs: false });

const props = withDefaults(
  defineProps<{
    defaultOpen?: boolean;
    open?: boolean;
  }>(),
  {
    defaultOpen: !defaultDocument?.cookie.includes(
      `${SIDEBAR_COOKIE_NAME}=false`,
    ),
    open: undefined,
  },
);

const emits = defineEmits<{
  'update:open': [open: boolean];
}>();

const isMobile = useMediaQuery('(max-width: 768px)');
const openMobile = ref(false);

const open = useVModel(props, 'open', emits, {
  defaultValue: props.defaultOpen ?? false,
  // Cast required: useVModel overloads discriminate on the literal type of `passive`.
  // At runtime this evaluates to `true` (uncontrolled) when `open` prop is omitted.
  passive: (props.open === undefined) as false,
}) as Ref<boolean>;

function setOpen(value: boolean) {
  open.value = value; // emits('update:open', value)

  // This sets the cookie to keep the sidebar state.
  if (defaultDocument) {
    defaultDocument.cookie = `${SIDEBAR_COOKIE_NAME}=${open.value}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`;
  }
}

function setOpenMobile(value: boolean) {
  openMobile.value = value;
}

// Helper to toggle the sidebar.
function toggleSidebar() {
  return isMobile.value
    ? setOpenMobile(!openMobile.value)
    : setOpen(!open.value);
}

useEventListener('keydown', (event: KeyboardEvent) => {
  if (
    event.key === SIDEBAR_KEYBOARD_SHORTCUT &&
    (event.metaKey || event.ctrlKey)
  ) {
    event.preventDefault();
    toggleSidebar();
  }
});

// We add a state so that we can do data-state="expanded" or "collapsed".
// This makes it easier to style the sidebar with Tailwind classes.
const state = computed(() => (open.value ? 'expanded' : 'collapsed'));

provideSidebarContext({
  state,
  open,
  setOpen,
  isMobile,
  openMobile,
  setOpenMobile,
  toggleSidebar,
});
</script>

<template>
  <TooltipProvider :delay-duration="0">
    <div
      v-bind="$attrs"
      data-slot="sidebar-wrapper"
      :style="{
        '--sidebar-width': SIDEBAR_WIDTH,
        '--sidebar-width-icon': SIDEBAR_WIDTH_ICON,
      }"
      :class="
        cn(
          'group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full',
          $attrs.class ?? '',
        )
      "
    >
      <slot />
    </div>
  </TooltipProvider>
</template>
