/**
 * lib/string-utils.ts — String transformations for dev CLIs.
 * Ported from SmartStack.cli/src/mcp/lib/string-utils.ts.
 *
 * Centralizes case conversions and naming helpers used by scaffolders,
 * validators and analyzers that reason about SmartStack naming conventions.
 */

/**
 * Convert PascalCase/camelCase to kebab-case. Handles consecutive uppercase letters.
 *   "HumanResources" → "human-resources"
 *   "APIClient" → "api-client"
 *   "HRDepartment" → "hr-department"
 */
export function toKebabCase(segment: string): string {
  return segment
    .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
    .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
    .toLowerCase();
}

/**
 * Convert kebab-case (or already camelCase) to camelCase.
 *   "human-resources" → "humanResources"
 *   "employees" → "employees"
 */
export function toCamelCase(segment: string): string {
  return segment.replace(/-([a-z0-9])/g, (_, c: string) => c.toUpperCase());
}

/**
 * Convert any case to PascalCase.
 *   "human-resources" → "HumanResources"
 *   "humanResources"  → "HumanResources"
 */
export function toPascalCase(segment: string): string {
  const camel = toCamelCase(segment);
  return camel.charAt(0).toUpperCase() + camel.slice(1);
}

/** Capitalize the first letter. */
export function capitalize(str: string): string {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

/**
 * Simple English singularization. Handles common plural forms.
 *   "employees" → "employee"
 *   "companies" → "company"
 *   "processes" → "process"
 *   "addresses" → "address"
 *   "status" → "status"
 */
export function singularize(word: string): string {
  if (word.endsWith('ies')) return word.slice(0, -3) + 'y';
  if (word.endsWith('sses')) return word.slice(0, -2);
  if (
    word.endsWith('shes') ||
    word.endsWith('ches') ||
    word.endsWith('xes') ||
    word.endsWith('zes')
  ) {
    return word.slice(0, -2);
  }
  if (word.endsWith('ses')) return word.slice(0, -1);
  if (word.endsWith('s') && !word.endsWith('ss')) return word.slice(0, -1);
  return word;
}

/**
 * Very rough English pluralization (inverse of singularize).
 *   "employee" → "employees"
 *   "company"  → "companies"
 *   "address"  → "addresses"
 */
export function pluralize(word: string): string {
  if (word.endsWith('y') && !/[aeiou]y$/.test(word)) return word.slice(0, -1) + 'ies';
  if (word.endsWith('s') || word.endsWith('x') || word.endsWith('z')) return word + 'es';
  if (word.endsWith('sh') || word.endsWith('ch')) return word + 'es';
  return word + 's';
}

/**
 * Convert a NavRoute dot-path to a URL path with kebab-case segments.
 *   "humanResources.employees" → "human-resources/employees"
 */
export function navRouteToUrlPath(navRoute: string): string {
  return navRoute.split('.').map(toKebabCase).join('/');
}

const FRENCH_DETERMINERS = new Set([
  'le', 'la', 'les', 'l',
  'un', 'une', 'des',
  'de', 'du', 'd',
  'a', 'au', 'aux',
  'et', 'ou',
]);

/**
 * Slugify a human-readable role label into a functional kebab-case code.
 * Used by Phase 0 Core Foundation Seed to derive Role.code from actor.label,
 * avoiding propagation of BA analysis codes (e.g. "BA-002-AC-008") that have
 * no business meaning at the domain level.
 *
 *   "Gestionnaire de budget"      → "gestionnaire-budget"
 *   "Contrôleur de gestion"        → "controleur-gestion"
 *   "L'agent des comptes clients" → "agent-comptes-clients"
 *
 * Pipeline: NFD-strip accents (Unicode combining marks U+0300..U+036F) →
 * lowercase → tokenise on non-alphanumeric →
 * drop French determiners → join with '-'. If every token is a determiner
 * (pathological case like label="Le"), keep the original tokens so we never
 * return an empty string for a non-empty input.
 */
export function slugifyRoleCode(label: string): string {
  if (!label || !label.trim()) return '';
  const ascii = label.normalize('NFD').replace(/[̀-ͯ]/g, '');
  const tokens = ascii.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
  if (tokens.length === 0) return '';
  const meaningful = tokens.filter((t) => !FRENCH_DETERMINERS.has(t));
  const finalTokens = meaningful.length > 0 ? meaningful : tokens;
  return finalTokens.join('-');
}

/**
 * Disambiguate a list of slugs by appending `-2`, `-3`, ... to duplicates so
 * that every output is unique. Order is preserved — the first occurrence keeps
 * its plain slug, subsequent collisions get numeric suffixes.
 *
 *   ["manager", "manager", "controleur", "manager"]
 *     → ["manager", "manager-2", "controleur", "manager-3"]
 */
export function disambiguateRoleCodes(slugs: string[]): string[] {
  const seen = new Map<string, number>();
  return slugs.map((s) => {
    const count = (seen.get(s) ?? 0) + 1;
    seen.set(s, count);
    return count === 1 ? s : `${s}-${count}`;
  });
}
