import { z } from "zod";
const codes = (...points: number[]) =>
  points.map((point) => String.fromCodePoint(point)).join("");
const range = (from: number, to: number) =>
  codes(...Array.from({ length: to - from + 1 }, (_, index) => from + index));
const directionOverride = new RegExp(
  `[${range(0x202a, 0x202e)}${range(0x2066, 0x2069)}]`,
  "u",
);
const kept = new Set([...codes(0x200c, 0x200d, 0x200e, 0x200f, 0x061c)]);
const hangulFillers = new RegExp(
  `[${codes(0x115f, 0x1160, 0x3164, 0xffa0)}]`,
  "gu",
);
export const cleanText = (value: string) =>
  value
    .replace(/\p{Cf}/gu, (character) =>
      kept.has(character) || directionOverride.test(character) ? character : "",
    )
    .replace(hangulFillers, "")
    .trim();
export const hasVisibleText = (value: string) =>
  /[\p{L}\p{N}]/u.test(cleanText(value));
export const singleLineText = (max: number) =>
  z
    .string()
    .overwrite(cleanText)
    .max(max, `Use no more than ${max} characters.`)
    .refine((value) => !/\p{Cc}/u.test(value), "Use a single line.")
    .refine(
      (value) => !directionOverride.test(value),
      "Remove hidden text-direction characters.",
    );
export const multiLineText = (max: number) =>
  z
    .string()
    .overwrite((value) => cleanText(value.replace(/\r\n?/g, "\n")))
    .max(max, `Use no more than ${max} characters.`)
    .refine(
      (value) => !/[^\P{Cc}\n\t]/u.test(value),
      "Remove hidden control characters.",
    )
    .refine(
      (value) => !directionOverride.test(value),
      "Remove hidden text-direction characters.",
    );
