import type { ReactNode } from "react";
import type { PromptMentionItem, PromptMentionTokenRange } from "./workbenchConstants";

export function escapeRegExp(value: string) {
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

export function normalizePromptWhitespace(value: string) {
  return value.replace(/[ \t]{2,}/g, " ").trim();
}

export function removePromptMentionTokenFromText(text: string, token: string) {
  if (!token) return text;
  const escapedToken = escapeRegExp(token);
  return normalizePromptWhitespace(
    text.replace(new RegExp(`(^|\\s)${escapedToken}(?=\\s|$)`, "g"), " "),
  );
}

export function removePromptTextRange(text: string, start: number, end: number) {
  return normalizePromptWhitespace(`${text.slice(0, start)}${text.slice(end)}`);
}

export function getPromptMentionTokenRanges(text: string, items: PromptMentionItem[]): PromptMentionTokenRange[] {
  if (!text || !items.length) return [];
  const ranges: PromptMentionTokenRange[] = [];
  for (const item of items) {
    const idx = text.indexOf(item.token);
    if (idx >= 0) {
      ranges.push({ start: idx, end: idx + item.token.length, item });
    }
  }
  return ranges.sort((a, b) => a.start - b.start);
}

export function renderPromptPreviewNodes(
  text: string,
  items: PromptMentionItem[],
): ReactNode[] {
  if (!text) return [];

  const tokens = Array.from(new Set(items.map((item) => item.token))).sort((a, b) => b.length - a.length);
  const tokenMap = new Map(items.map((item) => [item.token, item]));
  const nodes: ReactNode[] = [];
  let cursor = 0;
  let index = 0;

  while (cursor < text.length) {
    const matchedToken = tokens.find((token) => text.startsWith(token, cursor));

    if (matchedToken) {
      const matchedItem = tokenMap.get(matchedToken);
      if (matchedItem) {
        nodes.push(
          <span key={`mention-${index}`} className="wb-prompt-mention-chip" data-token={matchedItem.token}>
            {matchedItem.token}
          </span>,
        );
        cursor += matchedToken.length;
        index += 1;
        continue;
      }
    }

    const nextTokenStart = tokens.reduce((closest, token) => {
      const pos = text.indexOf(token, cursor + 1);
      return pos >= 0 && (closest < 0 || pos < closest) ? pos : closest;
    }, -1);

    const end = nextTokenStart >= 0 ? nextTokenStart : text.length;
    const segment = text.slice(cursor, end);
    if (segment) {
      nodes.push(<span key={`text-${index}`}>{segment}</span>);
      index += 1;
    }
    cursor = end;
  }

  return nodes;
}