import has from 'lodash/has';

function hasKey<T>(
  item: T,
  maybeKeyOfT: string | keyof T,
): maybeKeyOfT is keyof T {
  return has(item, maybeKeyOfT);
}

export type DataPropertyMap<T> = Record<string, (keyof T)[]>;

const getValueAt =
  <T>(item: T, dataPropertyMap?: DataPropertyMap<T>) =>
  (maybeCurlyProperty: string) => {
    const maybeCurlySegment = /^{(.*)}$/.exec(maybeCurlyProperty)?.[1];
    if (maybeCurlySegment) {
      const maybeKeyOfT =
        dataPropertyMap == null
          ? maybeCurlySegment
          : dataPropertyMap[maybeCurlySegment]?.find((prop) =>
              hasKey(item, prop),
            );

      if (maybeKeyOfT != null && hasKey(item, maybeKeyOfT)) {
        return String(item[maybeKeyOfT]);
      }
    }

    return maybeCurlyProperty;
  };

export function replaceUrlCurlySegments<T>(
  templateUrl: URL,
  item: T,
  dataPropertyMap?: DataPropertyMap<T>,
) {
  const url = new URL(templateUrl.href);

  url.pathname = templateUrl.pathname
    .split('/')
    .map(decodeURIComponent)
    .map(getValueAt(item, dataPropertyMap))
    .map(encodeURIComponent)
    .join('/');

  const getItemValueAt = getValueAt(item, dataPropertyMap);

  for (const [key, value] of Array.from(templateUrl.searchParams.entries())) {
    url.searchParams.set(key, getItemValueAt(value));
  }

  return url;
}
