// usernameUtils.js — username normalization and fuzzy matching

// Usernames containing this string are admin/system accounts — skip display entirely
const ADMIN_PATTERN = /admin/i;

/**
 * Normalizes a raw Windows/AzureAD username for display.
 *
 * Returns null for:
 *   - empty/missing values
 *   - admin/system accounts (username contains "admin", e.g. deRAdmin, DAdmin)
 *
 * Handles:
 *   - Domain prefix stripping:  DOMAIN\user  or  AzureAD\KristinMcHugh
 *   - CamelCase splitting:       KristinMcHugh  →  Kristin Mc Hugh
 *   - Word capitalization:       johndaigle  →  Johndaigle,  michael  →  Michael
 *
 * Note: all-lowercase concatenated names (johndaigle) can't be split without
 * external knowledge — we just capitalize the first letter.  If a contact name
 * matches via fuzzy comparison the caller should prefer the contact's name.
 *
 * @param {string} raw  — value from kabuto_information.last_user
 * @returns {string|null}
 */
export function normalizeUsername(raw) {
  if (!raw) return null;

  // Strip domain prefix (handles both DOMAIN\user and AzureAD\Name)
  let name = raw.includes('\\') ? raw.split('\\').pop() : raw;
  if (!name) return null;

  // Skip admin/system accounts — don't display or match these
  if (ADMIN_PATTERN.test(name)) return null;

  // If no spaces already, try CamelCase split
  // e.g. KristinMcHugh → Kristin Mc Hugh
  if (!name.includes(' ')) {
    name = name.replace(/([a-z])([A-Z])/g, '$1 $2');
  }

  // Capitalize first letter of each word (leave remaining letters as-is to
  // preserve intentional casing like McHugh after a manual split)
  name = name
    .split(' ')
    .filter(Boolean)
    .map(w => w.charAt(0).toUpperCase() + w.slice(1))
    .join(' ');

  return name || null;
}

/**
 * Fuzzy-matches a (possibly CamelCase or space-separated) username against a
 * contact's full name by stripping all spaces and comparing case-insensitively.
 *
 *   "KristinMcHugh"  ↔  "Kristin McHugh"  →  true   (both → "kristinmchugh")
 *   "johndaigle"     ↔  "John Daigle"      →  true   (both → "johndaigle")
 *   "michael"        ↔  "Michael Smith"    →  false  (different after strip)
 *
 * @param {string} username      — raw value from kabuto_information.last_user
 * @param {string} contactName   — contact full name from Syncro
 * @returns {boolean}
 */
export function usernameFuzzyMatch(rawUsername, contactName) {
  if (!rawUsername || !contactName) return false;
  // Strip domain prefix first so DOMAIN\johndaigle compares as johndaigle
  const stripDomain = s => s.includes('\\') ? s.split('\\').pop() : s;
  const norm = s => stripDomain(s).toLowerCase().replace(/\s+/g, '');
  return norm(rawUsername) === norm(contactName);
}

/**
 * Single-name matching: if the username normalizes to a single word (e.g. "michael"),
 * checks whether that word uniquely matches the first name of exactly one distinct
 * contact across all contact names in the company. If so, and the assigned contact's
 * first name matches, returns true.
 *
 *   username="michael", contactName="Michael Smith", allContactNames=["Michael Smith"]
 *   → true  (only one "michael" first name in the company)
 *
 *   username="michael", allContactNames=["Michael Smith", "Michael Jones"]
 *   → false (ambiguous — two Michaels)
 *
 * @param {string}   rawUsername      — raw value from kabuto_information.last_user
 * @param {string}   contactName      — contact full name of the assigned contact
 * @param {string[]} allContactNames  — all distinct contact names in the customer
 * @returns {boolean}
 */
export function usernameFirstNameMatch(rawUsername, contactName, allContactNames) {
  if (!rawUsername || !contactName || !allContactNames?.length) return false;

  const normalized = normalizeUsername(rawUsername);
  if (!normalized) return false;

  // Only apply to single-word usernames (multi-word is already handled by fuzzy match)
  if (normalized.includes(' ')) return false;

  const lowerUser = normalized.toLowerCase();

  // Assigned contact's first name must match the username
  const assignedFirst = contactName.split(' ')[0].toLowerCase();
  if (lowerUser !== assignedFirst) return false;

  // Count how many distinct contacts share that first name
  const distinct = new Set(allContactNames.map(n => n.toLowerCase()));
  const matches  = [...distinct].filter(cn => cn.split(' ')[0] === lowerUser);
  return matches.length === 1;
}

/**
 * Initial-plus-lastname matching: handles usernames like "jgallerani" that are
 * constructed as first-initial + full last name.
 *
 *   "jgallerani"  ↔  "James Gallerani"  →  true   (j + gallerani)
 *   "jsmith"      ↔  "John Smith"       →  true only if unique in company
 *
 * Requires uniqueness across the company to avoid false positives.
 *
 * @param {string}   rawUsername      — raw value from kabuto_information.last_user
 * @param {string}   contactName      — contact full name of the assigned contact
 * @param {string[]} allContactNames  — all distinct contact names in the customer
 * @returns {boolean}
 */
export function usernameInitialLastNameMatch(rawUsername, contactName, allContactNames) {
  if (!rawUsername || !contactName || !allContactNames?.length) return false;

  const normalized = normalizeUsername(rawUsername);
  if (!normalized || normalized.includes(' ')) return false;

  const lowerUser = normalized.toLowerCase();

  // Build pattern: first initial + last name word
  // e.g. "James Gallerani" → "jgallerani", "Mary Ann Smith" → "msmith"
  const _buildPattern = name => {
    const parts = name.toLowerCase().split(' ').filter(Boolean);
    if (parts.length < 2) return null;
    return parts[0][0] + parts[parts.length - 1];
  };

  const assignedPattern = _buildPattern(contactName);
  if (!assignedPattern || lowerUser !== assignedPattern) return false;

  // Require uniqueness: prevent matching when multiple contacts share the same pattern
  const distinct   = new Set(allContactNames.map(n => n.toLowerCase()));
  const matchCount = [...distinct].filter(cn => _buildPattern(cn) === lowerUser).length;
  return matchCount === 1;
}

/**
 * First-name-plus-initials matching: handles usernames like "johnd" that are
 * constructed as firstname + first-letter(s) of last name parts.
 *
 *   "johnd"   ↔  "John Daigle"   →  true   (john + d)
 *   "johnd"   ↔  "John Davis"    →  still true if only one "johnd" pattern in company
 *   "johnd"   ↔  "Jane Doe"      →  false  (jane ≠ john)
 *
 * Only fires when the username is a single word and doesn't already match via
 * fuzzy or first-name matching. Requires uniqueness across the company.
 *
 * @param {string}   rawUsername      — raw value from kabuto_information.last_user
 * @param {string}   contactName      — contact full name of the assigned contact
 * @param {string[]} allContactNames  — all distinct contact names in the customer
 * @returns {boolean}
 */
export function usernameNameInitialMatch(rawUsername, contactName, allContactNames) {
  if (!rawUsername || !contactName || !allContactNames?.length) return false;

  const normalized = normalizeUsername(rawUsername);
  if (!normalized || normalized.includes(' ')) return false;

  const lowerUser = normalized.toLowerCase();

  // Build the expected pattern for the assigned contact: firstname + initials
  // e.g. "John Daigle" → "johnd", "Mary Ann Smith" → "maryans"
  const _buildPattern = name => {
    const parts = name.toLowerCase().split(' ').filter(Boolean);
    if (parts.length < 2) return null;
    return parts[0] + parts.slice(1).map(p => p[0]).join('');
  };

  const assignedPattern = _buildPattern(contactName);
  if (!assignedPattern || lowerUser !== assignedPattern) return false;

  // Check uniqueness: how many contacts produce the same pattern?
  const distinct = new Set(allContactNames.map(n => n.toLowerCase()));
  const matchCount = [...distinct].filter(cn => _buildPattern(cn) === lowerUser).length;
  return matchCount === 1;
}
