// eslint-disable-next-line no-useless-escape
import { DomUtils, parseDocument } from 'htmlparser2';
import qs from 'qs';
import { getParagraphs } from 'tag-users';

import { LinkPreviewResponse } from 'src/types/previewLink';
import { getFullLink } from 'src/utils/links';

export const urlRegex =
  /((?:(http|https|Http|Https|rtsp|Rtsp):\/\/(?:(?:[a-zA-Z0-9\$\-\_\.\+\!\*\'\(\)\,\;\?\&\=]|(?:\%[a-fA-F0-9]{2})){1,64}(?:\:(?:[a-zA-Z0-9\$\-\_\.\+\!\*\'\(\)\,\;\?\&\=]|(?:\%[a-fA-F0-9]{2})){1,25})?\@)?)?((?:(?:[a-zA-Z0-9][a-zA-Z0-9\-]{0,64}\.)+(?:(?:aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(?:biz|b[abdefghijmnorstvwyz])|(?:cat|com|gle|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(?:edu|e[cegrstu])|f[ijkmor]|(?:gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(?:info|int|i[delmnoqrst])|(?:jobs|j[emop])|k[eghimnrwyz]|l[abcikrstuvy]|(?:mil|mobi|museum|m[acdghklmnopqrstuvwxyz])|(?:name|net|n[acefgilopruz])|(?:org|om)|(?:pro|p[aefghklmnrstwy])|qa|r[eouw]|s[abcdeghijklmnortuvyz]|(?:tel|travel|t[cdfghjklmnoprtvwz])|u[agkmsyz]|v[aceginu]|w[fs]|y[etu]|z[amw]))|(?:(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])))(?:\:\d{1,5})?)(\/(?:(?:[a-zA-Z0-9\;\/\?\:\@\&\=\#\~\-\.\+\!\*\'\(\)\,\_])|(?:\%[a-fA-F0-9]{2}))*)?(?:\b|$)/gi;

export const formatLinkPreviewUrl = (url: string, hl: string = 'en') => {
  if (!url) return '';

  let [link, params] = url.split('?');

  const parsedParams = qs.parse(params);
  parsedParams.hl = hl;

  return link + qs.stringify(parsedParams, { addQueryPrefix: true });
};

/**
 * Validates if a string matches a proper email address format
 * @param text - The text to validate
 * @returns true if the text matches email format (local@domain.tld)
 */
const isValidEmailFormat = (text: string): boolean => {
  if (!text) return false;

  // Basic email regex: local-part@domain.tld
  // Allows letters, numbers, dots, hyphens, underscores, plus signs before @
  // Requires at least one dot in the domain part
  const emailRegex = /^[a-zA-Z0-9._+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;

  return emailRegex.test(text);
};

/**
 * Checks if a text string is an email address or email link
 * @param text - The text to check
 * @returns true if the text is an email address or email link
 */
export const isEmailAddress = (text: string): boolean => {
  if (!text) return false;

  const lowerText = text.toLowerCase();

  // Check for mailto: links
  if (lowerText.startsWith('mailto:')) {
    return true;
  }

  // Validate proper email format (not just checking for @)
  if (isValidEmailFormat(text)) {
    return true;
  }

  return false;
};

export const matchWithUrl = (value: string) => {
  if (!value) return [];

  const matches = value.matchAll(urlRegex);

  return [...matches]
    .filter(match => {
      const matchedText = match[0];

      // If the match already starts with http:// or https://, it's definitely a URL
      const lowerMatchedText = matchedText.toLowerCase();
      if (
        lowerMatchedText.startsWith('http://') ||
        lowerMatchedText.startsWith('https://') ||
        lowerMatchedText.startsWith('www.')
      ) {
        return true;
      }

      // Check if the match is part of an email address
      if (isValidEmailFormat(matchedText)) {
        return false;
      }

      return true;
    })
    .map(match => getFullLink({ link: match[0], withHttp: true }));
};

export const getPreviewLink = (body: string): string[] => {
  let urls: string[] = [];

  if (body) {
    getParagraphs(body).forEach((paragraph: { text: string }[]) =>
      paragraph.forEach(postComponents =>
        urls.push(...matchWithUrl(postComponents.text)),
      ),
    );
  }

  return urls;
};

export const getPreviewLinkHTML = (bodyHtml: string): string[] => {
  const document = parseDocument(bodyHtml);

  const links = DomUtils.findAll(
    elem => elem.name === 'a' && !!elem.attribs && !!elem.attribs.href,
    document.children,
  );

  const nonMentionLinks = links
    .filter(link => !link.attribs.class?.includes('mention'))
    .map(link => link.attribs.href)
    .filter(href => {
      if (!href) return false;

      const lowerHref = href.toLowerCase();

      // If the href already starts with http:// or https://, it's definitely a URL
      if (lowerHref.startsWith('http://') || lowerHref.startsWith('https://')) {
        return true;
      }

      // Filter out email addresses and email links
      if (isEmailAddress(href)) {
        return false;
      }

      return true;
    });

  return nonMentionLinks;
};

export const getMetadata = (
  linkPreviews: Record<string, LinkPreviewResponse>,
): LinkPreviewResponse | null =>
  Object.keys(linkPreviews).length !== 0
    ? Object.entries(linkPreviews)[0][1]
    : null;
