export function json(content: unknown) {
  const headers = new Headers();
  headers.append("Content-Type", "application/json");
  return new Response(JSON.stringify(content), {
    headers,
  });
}

export const isValidUrl = (urlString: string) => {
  try {
    return Boolean(new URL(urlString));
  } catch (_e) {
    return false;
  }
};

export const fixRenderedMarkdown = (content: string) => {
  return content.replace("***\n", "---")
    .replace("----------------", "---")
    .replace("\n---", "---")
    .replace(/^(date:[^'\n]*)'|'/gm, (match, p1, p2) => {
      if (p1) {
        // This is a line starting with date: followed by single quotes
        return p1.replace(/'/gm, "");
      } else if (p2) {
        return "";
      } else {
        // This is a line with single quotes, but not starting with date:
        return match;
      }
    });
};

export async function fetchStream(url: string, cb: (chunk: string) => void) {
  const response = await fetch(url);
  const reader = response?.body?.getReader();
  if (reader) {
    while (true) {
      const { done, value } = await reader.read();
      if (done) return;
      const data = new TextDecoder().decode(value);
      data
        .split("$")
        .filter((d) => d && d.length)
        .map((d) => cb(Array.isArray(d) ? d[0] : d));
    }
  }
}

export function hashString(message: string) {
  let hash = 0;
  for (let i = 0; i < message.length; i++) {
    const char = message.charCodeAt(i);
    hash = ((hash << 5) - hash) + char;
    hash = hash & hash; // Convert to 32bit integer
  }
  return hash;
}

export const createStreamResponse = () => {
  let controller: ReadableStreamController<ArrayBufferView>;
  const body = new ReadableStream({
    start(cont) {
      controller = cont;
    },
  });

  const response = new Response(body, {
    headers: {
      "content-type": "text/plain",
      "x-content-type-options": "nosniff",
    },
  });

  function cancel() {
    controller.close();
  }

  function enqueue(chunk: string) {
    controller?.enqueue(new TextEncoder().encode("$" + chunk));
  }

  return {
    response,
    cancel,
    enqueue,
  };
};

export type StreamResponse = ReturnType<typeof createStreamResponse>;

export function debounce<T extends (...args: Parameters<T>) => void>(
  this: ThisParameterType<T>,
  fn: T,
  delay = 300,
) {
  let timer: ReturnType<typeof setTimeout> | undefined;
  return (...args: Parameters<T>) => {
    clearTimeout(timer);
    timer = setTimeout(() => fn.apply(this, args), delay);
  };
}

export function parseRating(rating: string | number) {
  if (typeof rating === "string") {
    return [...rating.matchAll(/⭐/)].length;
  }
  return rating;
}
