const EMAIL_ADDRESS_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const DOMAIN_PATTERN = /^(?:www\.)?(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}(?::\d+)?(?:[/?#].*)?$/i;
const DISALLOWED_URL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f\s]/;
const RASTER_DATA_IMAGE_PATTERN = /^data:image\/(?:gif|jpeg|png|webp);base64,[a-z0-9+/=]+$/i;

export function normalizeWysiwygLinkUrl(value: string): string | null {
  const candidate = value.trim();
  if (!candidate || DISALLOWED_URL_CHARACTER_PATTERN.test(candidate)) return null;

  if (candidate.startsWith("#") || isRelativeUrl(candidate)) return candidate;
  if (EMAIL_ADDRESS_PATTERN.test(candidate)) return `mailto:${candidate}`;
  if (candidate.startsWith("//")) return normalizeAbsoluteUrl(`https:${candidate}`, LINK_PROTOCOLS);
  if (DOMAIN_PATTERN.test(candidate)) return normalizeAbsoluteUrl(`https://${candidate}`, LINK_PROTOCOLS);
  return normalizeAbsoluteUrl(candidate, LINK_PROTOCOLS);
}

export function normalizeWysiwygImageUrl(value: string): string | null {
  const candidate = value.trim();
  if (!candidate) return null;
  if (RASTER_DATA_IMAGE_PATTERN.test(candidate)) return candidate;
  if (DISALLOWED_URL_CHARACTER_PATTERN.test(candidate)) return null;
  if (/^cid:[^<>"']+$/i.test(candidate)) return candidate;
  if (isRelativeUrl(candidate)) return candidate;
  if (candidate.startsWith("//")) return normalizeAbsoluteUrl(`https:${candidate}`, IMAGE_PROTOCOLS);
  if (DOMAIN_PATTERN.test(candidate)) return normalizeAbsoluteUrl(`https://${candidate}`, IMAGE_PROTOCOLS);
  return normalizeAbsoluteUrl(candidate, IMAGE_PROTOCOLS);
}

const LINK_PROTOCOLS = new Set(["http:", "https:", "mailto:", "tel:"]);
const IMAGE_PROTOCOLS = new Set(["http:", "https:"]);

function isRelativeUrl(value: string): boolean {
  return value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value.startsWith("?");
}

function normalizeAbsoluteUrl(value: string, allowedProtocols: Set<string>): string | null {
  try {
    const parsed = new URL(value);
    if (!allowedProtocols.has(parsed.protocol.toLowerCase())) return null;
    if ((parsed.protocol === "http:" || parsed.protocol === "https:") && !parsed.hostname) return null;
    return parsed.href;
  } catch {
    return null;
  }
}
