import { tryOnScopeDispose }          from '@vueuse/core';
import type { MaybeRefOrGetter, Ref } from 'vue';
import type { RouteMeta }             from 'vue-router';


export interface ReactiveRouteOptionsWithTransform<V, R> {
  priority?: 'query' | 'meta';
  route?: ReturnType<typeof useRoute>;
  transform?: (val: V) => R;
}

export function useRouteMeta<T extends RouteMeta = RouteMeta, K = T>(
  name: keyof T,
  defaultValue?: MaybeRefOrGetter,
  options: ReactiveRouteOptionsWithTransform<T, K> = {},
): Ref<K> {

  const {
    priority = 'meta',
    route = useRoute(),
    transform = value => value as any as K,
  } = options;

  let query = route.query[name] as any;
  let meta = route.meta[ name ] as any;

  tryOnScopeDispose(() => {
    query = undefined;
    meta = undefined;
  });


  let _trigger: () => void;
  const proxy = customRef<any>((track, trigger) => {
    _trigger = trigger;
    return {
      get() {
        track();
        const value = priority === 'query' && query ? query : meta;
        return transform(value !== undefined && value !== '' ? value : toValue(defaultValue));
      },
      set(v) {
        meta = v;
        trigger();
      },
    };
  });

  watch(
    () => route.meta[ name ],
    (v) => {
      if ( meta === v ) return;
      meta = v;
      _trigger();
    },
    { flush: 'sync' },
  );

  return proxy as Ref<K>;
}
