"use strict";

exports.__esModule = true;
exports.TYPE_SAMPLE_SIZE = void 0;
exports.detectColumnType = detectColumnType;
exports.detectValueType = detectValueType;
// Field-type detection for new fields created during CSV import (CAIRO-4131).
//
// Ported from cm-import (`wix-data-client/cm-import`):
//   - per-cell rules from `src/field-configuration/detectFieldConfig.js`
//     (`detectPrimitiveType`) and `src/types/validators/{number,url}.js`
//   - the column-level majority vote from
//     `src/field-configuration/guessFieldConfigFromSamples.js`
//
// This module owns the shared `NewFieldType` vocabulary and the primitive
// detector (number / date / boolean / url, else text). Media detection (image /
// video / document / audio) lives in `@wix/patterns-fields` — it needs the media
// URI parser, which core must not depend on — and is injected into `ImportState`
// via `detectColumnType` (see `ImportStateProps`). `detectColumnType` here takes
// the per-value detector as a parameter so the fields detector reuses this
// sampling/majority logic instead of re-implementing it.

// All field types a new column can be detected as. The primitive detector below
// only ever returns the first five; image/video/document/audio come from the
// injected fields detector.

// Infers a column's field type from its values. Injected into `ImportState`.

// Infers a single cell's field type.

// Column-detection sampling: look at the first `TYPE_SAMPLE_SIZE` values and
// require a >= `MAJORITY` share to agree on a non-text type. We use the same 2/3
// vote as cm-import but a **larger sample** — cm-import's `constants.sampleSize`
// is 6; we deliberately sample more so the inference is steadier on messy
// columns. Exported so callers pass only the head of a column to
// `detectColumnType` instead of copying it whole.
const TYPE_SAMPLE_SIZE = exports.TYPE_SAMPLE_SIZE = 50;
const MAJORITY = 2 / 3;

// cm-import `validators/number.js` `validateString`.
const NUMBER_RE = /^[+-]?(?:\d+\.?\d*)?$/;

// cm-import `detectFieldConfig.js` date patterns.
const SLASH_DATE_RE = /^\d{1,2}\/\d{1,2}\/\d{4}(?: \d{1,2}:\d{1,2})?$/;
const ISO_DATE_RE = /\d{4}-\d{2}-\d{2}/;

// Approximates cm-import `validators/url.js` (website / email / phone / relative
// links). cm-import delegates structural validation to `@wix/santa-core-utils`
// `isValidUrl`/`isValidEmail`, which aren't available here — so website URLs are
// validated with the platform `URL` parser instead (rejects empty/whitespace/
// malformed authorities). It's still an approximation: `URL` accepts some
// odd-but-parseable hosts santa would reject.
const WEBSITE_PROTOCOLS = ['http:', 'https:', 'ftp:'];
const EMAIL_PROTOCOL = 'mailto:';
const PHONE_PROTOCOLS = ['sms:', 'tel:'];
const RELATIVE_PREFIXES = ['/', '#', '?'];
const PHONE_RE = /^[\s\d()+\-/_]+$/;
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const startsWithAny = (value, prefixes) => prefixes.some(prefix => value.startsWith(prefix));
const isValidWebsiteUrl = value => {
  let parsed;
  try {
    parsed = new URL(value);
  } catch {
    return false;
  }
  return WEBSITE_PROTOCOLS.includes(parsed.protocol);
};
const isEmailUrl = value => value.startsWith(EMAIL_PROTOCOL) && EMAIL_RE.test(value.slice(EMAIL_PROTOCOL.length));
const isPhoneUrl = value => PHONE_PROTOCOLS.some(protocol => value.startsWith(protocol) && PHONE_RE.test(value.slice(protocol.length)));
const isUrl = value => isValidWebsiteUrl(value) || isEmailUrl(value) || isPhoneUrl(value) || startsWithAny(value, RELATIVE_PREFIXES);
const isDate = value => {
  if (SLASH_DATE_RE.test(value)) {
    if (!Number.isNaN(new Date(value).getTime())) {
      return true;
    }
    // cm-import also accepts dd/mm/yyyy by swapping day and month.
    const swapped = value.replace(/(\d{1,2})\/(\d{1,2})\/(\d{4})/, '$2/$1/$3');
    if (!Number.isNaN(new Date(swapped).getTime())) {
      return true;
    }
  }
  return ISO_DATE_RE.test(value) && !Number.isNaN(new Date(value).getTime());
};

/**
 * Infer the field type of a single CSV cell, in cm-import's order:
 * empty → number → boolean → date → url, else text.
 */
function detectValueType(value) {
  if (value === '') {
    return 'text';
  }
  if (NUMBER_RE.test(value)) {
    return 'number';
  }
  const lower = value.toLowerCase();
  if (lower === 'true' || lower === 'false') {
    return 'boolean';
  }
  if (isDate(value)) {
    return 'date';
  }
  if (isUrl(value)) {
    return 'url';
  }
  return 'text';
}

/**
 * Infer a column's field type from its first values: the most common detected
 * type, but only when it covers at least two-thirds of the samples (otherwise
 * `text`). Same majority-vote algorithm as cm-import
 * `guessFieldConfigFromSamples`, with a larger sample (see `TYPE_SAMPLE_SIZE`).
 *
 * `detectValue` defaults to the primitive {@link detectValueType}; the fields
 * detector passes a media-aware variant so it reuses this sampling/majority
 * logic.
 */
function detectColumnType(values, detectValue = detectValueType) {
  const samples = values.slice(0, TYPE_SAMPLE_SIZE);
  if (samples.length === 0) {
    return 'text';
  }
  const counts = new Map();
  samples.forEach(value => {
    const type = detectValue(value);
    counts.set(type, (counts.get(type) ?? 0) + 1);
  });
  let best = 'text';
  let bestCount = 0;
  counts.forEach((count, type) => {
    if (count > bestCount) {
      best = type;
      bestCount = count;
    }
  });
  return bestCount >= samples.length * MAJORITY ? best : 'text';
}
//# sourceMappingURL=detectFieldType.js.map