/**
 * Profilpflege für torchwood.app – Globale Informationen (18 Felder).
 * Befehle: kunde name Robin · fake beruf Lehrer · kunde name (nur fokussieren)
 */
const CHILLICOLLECTIVO_PROFILE_FIELDS = [
  { id: 'name', labels: ['Name'], aliases: ['name'] },
  { id: 'koerper', labels: ['Körper'], aliases: ['körper', 'koerper'] },
  { id: 'geburtstag', labels: ['Geburtstag'], aliases: ['geburtstag'] },
  { id: 'beruf', labels: ['Beruf'], aliases: ['beruf'] },
  { id: 'familie', labels: ['Familie'], aliases: ['familie'] },
  { id: 'kinder', labels: ['Kinder'], aliases: ['kinder'] },
  { id: 'haustiere', labels: ['Haustiere'], aliases: ['haustiere'] },
  { id: 'hobbys', labels: ['Hobbys'], aliases: ['hobbys', 'hobby'] },
  { id: 'letzte_beziehung', labels: ['Letzte Beziehung'], aliases: ['letzte beziehung'] },
  { id: 'sucht_nach', labels: ['Suche nach', 'Sucht nach', 'Kunde sucht'], aliases: ['suche nach', 'sucht nach', 'such nach', 'kunde sucht'] },
  { id: 'tattoo_piercing', labels: ['Tattoo / Piercing'], aliases: ['tattoo piercing', 'tattoo pircing', 'tattoo piercin', 'tattoos piercing', 'tattoo / piercing'] },
  { id: 'no_gos', labels: ['No-Gos'], aliases: ['no gos', 'no-gos', 'nogos'] },
  { id: 'vorlieben', labels: ['Vorlieben'], aliases: ['vorlieben'] },
  { id: 'vorstellungen', labels: ['Vorstellungen'], aliases: ['vorstellungen'] },
  { id: 'filme', labels: ['Filme'], aliases: ['filme', 'film'] },
  { id: 'musik', labels: ['Musik'], aliases: ['musik'] },
  { id: 'essen_trinken', labels: ['Essen / Trinken'], aliases: ['essen trinken', 'essen und trinken'] },
  { id: 'sonstiges', labels: ['Sonstiges'], aliases: ['sonstiges'] },
];

const CHILLICOLLECTIVO_PROFILE_FIELD_DEFAULT_ALIASES = Object.fromEntries(
  CHILLICOLLECTIVO_PROFILE_FIELDS.map((field) => [field.id, [...field.aliases]])
);

const CHILLICOLLECTIVO_SIDE_CUSTOMER = 'customer';
const CHILLICOLLECTIVO_SIDE_FAKE = 'fake';

let CHILLICOLLECTIVO_PROFILE_CMD_CONFIG = null;
let CHILLICOLLECTIVO_PROFILE_ALIAS_ORDER = [];

function chillicollectivoSplitAliasList(raw) {
  return String(raw || '')
    .split(/[,;|/]+/)
    .map((s) => s.trim())
    .filter(Boolean);
}

function chillicollectivoEnsureAliasList(raw, requiredRaw) {
  const out = [];
  const seen = new Set();
  const add = (item) => {
    const value = String(item || '').trim();
    const key = value.toLowerCase().replace(/\s+/g, ' ');
    if (!value || seen.has(key)) return;
    seen.add(key);
    out.push(value);
  };
  chillicollectivoSplitAliasList(raw).forEach(add);
  chillicollectivoSplitAliasList(requiredRaw).forEach(add);
  return out.join(', ');
}

function chillicollectivoEscapeRegex(value) {
  return String(value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

function chillicollectivoGetDefaultProfileCmdConfig() {
  return {
    sideCustomer: 'kunde',
    sideFake: 'fake',
    fieldAliases: Object.fromEntries(
      CHILLICOLLECTIVO_PROFILE_FIELDS.map((field) => [
        field.id,
        (CHILLICOLLECTIVO_PROFILE_FIELD_DEFAULT_ALIASES[field.id] || field.aliases).join(', '),
      ])
    ),
    ppKeyword: 'pp',
    ppAutoAliases: '',
    ppBothTriggers: '',
    undoAliases: 'undo, rückgängig, rueckgaengig, zurück, widerrufen',
    goExact: '',
    goHiddenStrip: '',
  };
}

function chillicollectivoSanitizeProfileCmdConfig(stored) {
  const defaults = chillicollectivoGetDefaultProfileCmdConfig();
  const cfg = { ...defaults, ...(stored && typeof stored === 'object' ? stored : {}) };
  cfg.sideCustomer = String(cfg.sideCustomer || defaults.sideCustomer).trim() || defaults.sideCustomer;
  cfg.sideFake = String(cfg.sideFake || defaults.sideFake).trim() || defaults.sideFake;
  cfg.ppKeyword = String(cfg.ppKeyword || defaults.ppKeyword).trim() || defaults.ppKeyword;
  cfg.ppAutoAliases = '';
  cfg.ppBothTriggers = '';
  cfg.undoAliases = String(cfg.undoAliases || defaults.undoAliases).trim() || defaults.undoAliases;
  cfg.goExact = '';
  cfg.goHiddenStrip = '';
  cfg.fieldAliases = { ...defaults.fieldAliases };
  const incomingFields = stored?.fieldAliases;
  if (incomingFields && typeof incomingFields === 'object') {
    for (const field of CHILLICOLLECTIVO_PROFILE_FIELDS) {
      if (incomingFields[field.id] != null) {
        cfg.fieldAliases[field.id] = String(incomingFields[field.id]).trim();
      }
    }
  }
  return cfg;
}

function chillicollectivoApplyProfileCmdConfig(stored) {
  const cfg = chillicollectivoSanitizeProfileCmdConfig(stored);
  CHILLICOLLECTIVO_PROFILE_CMD_CONFIG = cfg;

  for (const field of CHILLICOLLECTIVO_PROFILE_FIELDS) {
    const aliases = chillicollectivoSplitAliasList(cfg.fieldAliases[field.id]);
    field.aliases = aliases.length
      ? aliases
      : [...(CHILLICOLLECTIVO_PROFILE_FIELD_DEFAULT_ALIASES[field.id] || [field.id])];
  }

  CHILLICOLLECTIVO_PROFILE_ALIAS_ORDER = CHILLICOLLECTIVO_PROFILE_FIELDS
    .flatMap((field) => field.aliases.map((alias) => ({ field, alias })))
    .sort((a, b) => b.alias.length - a.alias.length);

  return cfg;
}

function chillicollectivoGetProfileCmdConfig() {
  if (!CHILLICOLLECTIVO_PROFILE_CMD_CONFIG) chillicollectivoApplyProfileCmdConfig(null);
  return CHILLICOLLECTIVO_PROFILE_CMD_CONFIG;
}

function chillicollectivoGetSideKeywords() {
  const cfg = chillicollectivoGetProfileCmdConfig();
  return {
    customer: String(cfg.sideCustomer || 'kunde').toLowerCase(),
    fake: String(cfg.sideFake || 'fake').toLowerCase(),
  };
}

function chillicollectivoSideFromKeyword(word) {
  const kw = chillicollectivoGetSideKeywords();
  const w = String(word || '').toLowerCase();
  if (w === kw.fake) return CHILLICOLLECTIVO_SIDE_FAKE;
  if (w === kw.customer) return CHILLICOLLECTIVO_SIDE_CUSTOMER;
  return null;
}

function chillicollectivoGetPrimaryFieldAlias(fieldId) {
  const field = CHILLICOLLECTIVO_PROFILE_FIELDS.find((f) => f.id === fieldId);
  return field?.aliases?.[0] || fieldId;
}

function chillicollectivoGetMishearingFixes() {
  const kw = chillicollectivoGetSideKeywords();
  const nameAlias = chillicollectivoGetPrimaryFieldAlias('name');
  const customerCmd = `${kw.customer} ${nameAlias}`;
  const fakeCmd = `${kw.fake} ${nameAlias}`;
  return [
    [/^(?:der\s+)?hundename\b/i, customerCmd],
    [/^(?:der\s+)?hunde\s+name\b/i, customerCmd],
    [/^(?:der\s+)?kundenname\b/i, customerCmd],
    [/^(?:der\s+)?kunden\s+name\b/i, customerCmd],
    [new RegExp(`^(?:der\\s+)?${chillicollectivoEscapeRegex(kw.customer)}\\s+nahme\\b`, 'i'), customerCmd],
    [new RegExp(`^(?:der\\s+)?${chillicollectivoEscapeRegex(kw.customer)}\\s+namen\\b`, 'i'), customerCmd],
    [new RegExp(`^(?:der\\s+)?${chillicollectivoEscapeRegex(kw.customer)}\\s+namm\\b`, 'i'), customerCmd],
    [new RegExp(`^${chillicollectivoEscapeRegex(kw.customer)}\\s+hundename\\b`, 'i'), customerCmd],
    [/^(?:der\s+)?fakename\b/i, fakeCmd],
    [new RegExp(`^(?:der\\s+)?${chillicollectivoEscapeRegex(kw.fake)}\\s+nahme\\b`, 'i'), fakeCmd],
    [new RegExp(`^(?:der\\s+)?${chillicollectivoEscapeRegex(kw.fake)}\\s+namen\\b`, 'i'), fakeCmd],
  ];
}

function chillicollectivoGetUndoTokens() {
  const cfg = chillicollectivoGetProfileCmdConfig();
  const tokens = new Set();
  for (const alias of chillicollectivoSplitAliasList(cfg.undoAliases)) {
    const normalized = chillicollectivoNormalizeUndoCommandText(alias);
    if (normalized) tokens.add(normalized);
  }
  return Array.from(tokens);
}

function chillicollectivoInitProfileCommandConfig() {
  chillicollectivoApplyProfileCmdConfig(null);
  if (typeof chrome === 'undefined' || !chrome.storage?.local) return;
  chrome.storage.local.get(['chillicollectivoProfileCommandConfig'], (data) => {
    if (data?.chillicollectivoProfileCommandConfig) {
      chillicollectivoApplyProfileCmdConfig(data.chillicollectivoProfileCommandConfig);
    }
  });
  try {
    chrome.storage.onChanged.addListener((changes, area) => {
      if (area === 'local' && changes.chillicollectivoProfileCommandConfig) {
        chillicollectivoApplyProfileCmdConfig(changes.chillicollectivoProfileCommandConfig.newValue);
      }
    });
  } catch (_) {}
}

function chillicollectivoIsTorchwoodPage() {
  if (document.documentElement?.dataset?.chillicollectivoTestHarbor === '1') return true;
  return /(^|\.)torchwood\.app$/i.test(window.location.hostname || '');
}

function chillicollectivoNormalizeProfileCommandText(text) {
  return (text || '')
    .replace(/\s+/g, ' ')
    .replace(/[„“"']/g, '')
    .replace(/\s*:\s*/g, ' ')
    .trim();
}

/** Spracherkennung: «kunde name» wird oft als «Hundename» verstanden. */
function chillicollectivoFixProfileCommandMishearings(text) {
  let t = chillicollectivoNormalizeProfileCommandText(text);
  if (!t) return t;

  for (const [pattern, replacement] of chillicollectivoGetMishearingFixes()) {
    if (pattern.test(t)) {
      t = t.replace(pattern, replacement);
      break;
    }
  }

  return t;
}

function chillicollectivoPrepareProfileCommandText(text) {
  return chillicollectivoFixProfileCommandMishearings(text);
}

function chillicollectivoNormalizeLabel(text) {
  return (text || '')
    .toLowerCase()
    .replace(/ß/g, 'ss')
    .replace(/[:\-/]/g, ' ')
    .replace(/\s+/g, ' ')
    .trim();
}

function chillicollectivoStripEmojisFromProfileText(text) {
  return String(text || '')
    .replace(/[\u{1F000}-\u{1FAFF}\u{2600}-\u{27BF}\u{2300}-\u{23FF}\u{2B50}\u{2934}-\u{2935}\u{3030}\u{303D}\u{3297}\u{3299}\u{FE0F}\u{200D}\u{1F1E6}-\u{1F1FF}]/gu, '')
    .replace(/\s+/g, ' ')
    .trim();
}

function chillicollectivoCleanProfileValue(value) {
  return chillicollectivoStripEmojisFromProfileText((value || '').trim().replace(/[.,!?;:]+$/g, '').trim());
}

function chillicollectivoNormalizeProfileValueToken(value) {
  return String(value || '')
    .toLowerCase()
    .replace(/ä/g, 'ae')
    .replace(/ö/g, 'oe')
    .replace(/ü/g, 'ue')
    .replace(/ß/g, 'ss')
    .replace(/\bfreundschaft\s*\+\b/g, 'freundschaft plus')
    .replace(/[^\p{L}\p{N}+]+/gu, ' ')
    .replace(/\s+/g, ' ')
    .trim();
}

function chillicollectivoProfileValueContains(current, value) {
  const want = chillicollectivoNormalizeProfileValueToken(value);
  if (!want) return true;
  const have = chillicollectivoNormalizeProfileValueToken(current);
  if (!have) return false;
  if (have === want || have.includes(want)) return true;
  return have.split(/\s*,\s*|\s*;\s*|\s*\|\s*/).some((part) => chillicollectivoNormalizeProfileValueToken(part) === want);
}

function chillicollectivoAppendProfileValue(current, value) {
  const cleanCurrent = chillicollectivoCleanProfileValue(current);
  const cleanValue = chillicollectivoCleanProfileValue(value);
  if (!cleanValue) return cleanCurrent;
  if (!cleanCurrent || cleanCurrent === '-') return cleanValue;
  if (chillicollectivoProfileValueContains(cleanCurrent, cleanValue)) return cleanCurrent;
  return `${cleanCurrent}, ${cleanValue}`;
}

function chillicollectivoInferProfileFactFromValue(value) {
  const raw = chillicollectivoCleanProfileValue(value);
  const normValue = chillicollectivoNormalizeProfileValueToken(raw);
  if (!raw || !normValue) return null;

  const hobbyRules = [
    [/^(?:ich\s+)?(?:gehe\s+)?(?:gerne\s+)?angeln$|^angelt$|^angler$|^angeln$/i, 'Angeln'],
    [/^(?:ich\s+)?(?:gehe\s+)?(?:gerne\s+)?rodeln$|^rodelt$|^rodeln$/i, 'Rodeln'],
    [/^(?:ich\s+)?(?:gehe\s+)?(?:gerne\s+)?ski\s*fahren$|^skifahren$|^ski$/i, 'Ski fahren'],
    [/^geocaching$/i, 'Geocaching'],
  ];
  for (const [pattern, normalized] of hobbyRules) {
    if (pattern.test(raw) || pattern.test(normValue)) {
      return { fieldId: 'hobbys', value: normalized, reason: 'inferred-hobby' };
    }
  }

  if (/pistazien\s*eis|pistazieneis/i.test(raw)) {
    return { fieldId: 'essen_trinken', value: 'Pistazieneis', reason: 'inferred-food' };
  }
  if (/\bmechatroniker(?:in)?\b/i.test(raw)) {
    return { fieldId: 'beruf', value: /in\b/i.test(raw) ? 'Mechatronikerin' : 'Mechatroniker', reason: 'inferred-job' };
  }
  if (/freundschaft\s*(?:\+|plus)|\bf\s*\+\b|\bf\+\b/i.test(raw)) {
    return { fieldId: 'sucht_nach', value: 'Freundschaft Plus', reason: 'inferred-search' };
  }
  if (/\bbeziehung\b|feste\s+beziehung|partnerschaft/i.test(raw)
    || /\b(?:frau|mann)\s+f[uueü]rs?\s+leben\b/i.test(raw)
    || /\bf[uueü]rs?\s+leben\s+finden\b/i.test(raw)
    || /\blebenspartner(?:in)?\b/i.test(raw)) {
    return { fieldId: 'sucht_nach', value: 'Beziehung', reason: 'inferred-search' };
  }
  if (/\bpille\b/i.test(raw)) {
    return { fieldId: 'sonstiges', value: 'Pille', reason: 'inferred-pill' };
  }
  if (/\b(?:single|ledig|geschieden|getrennt)\b/i.test(raw) || /\b\d+\s+jahre?\s+single\b/i.test(raw)) {
    return { fieldId: 'letzte_beziehung', value: raw, reason: 'inferred-relationship-state' };
  }
  if (/^[A-ZÄÖÜ][a-zäöüß]{1,30}(?:\s+[A-ZÄÖÜ][a-zäöüß]{1,30})?$/.test(raw)
    || (/^[a-zäöüß]{2,30}$/.test(raw) && !/(angeln|rodeln|ski|geocaching|wandern|schwimmen|beziehung|single|pille)/i.test(raw))) {
    return { fieldId: 'name', value: raw.replace(/^\p{Ll}/u, (ch) => ch.toUpperCase()), reason: 'inferred-name' };
  }
  return null;
}

function chillicollectivoLooksLikeProfileCommand(text) {
  const kw = chillicollectivoGetSideKeywords();
  const pattern = new RegExp(`^(${chillicollectivoEscapeRegex(kw.customer)}|${chillicollectivoEscapeRegex(kw.fake)})\\s+`, 'i');
  return pattern.test(chillicollectivoPrepareProfileCommandText(text));
}

function chillicollectivoParseProfileCommand(text) {
  const normalized = chillicollectivoPrepareProfileCommandText(text);
  if (!normalized) return null;

  const kw = chillicollectivoGetSideKeywords();
  const sideMatch = normalized.match(
    new RegExp(`^(${chillicollectivoEscapeRegex(kw.customer)}|${chillicollectivoEscapeRegex(kw.fake)})\\s+(.+)$`, 'i')
  );
  if (!sideMatch) return null;

  const side = chillicollectivoSideFromKeyword(sideMatch[1]);
  if (!side) return null;
  const rest = sideMatch[2].trim();

  for (const { field, alias } of CHILLICOLLECTIVO_PROFILE_ALIAS_ORDER) {
    const pattern = new RegExp(`^${chillicollectivoEscapeRegex(alias)}(?:\\s*[:：-]\\s*|\\s+(.+)|$)(.*)$`, 'i');
    const match = rest.match(pattern);
    if (!match) continue;

    const rawValue = match[1] || match[2] || '';
    return {
      side,
      fieldId: field.id,
      fieldLabel: field.labels[0],
      value: chillicollectivoCleanProfileValue(rawValue),
    };
  }

  const inferred = chillicollectivoInferProfileFactFromValue(rest);
  if (inferred) {
    const field = CHILLICOLLECTIVO_PROFILE_FIELDS.find((item) => item.id === inferred.fieldId);
    if (!field) return null;
    return {
      side,
      fieldId: field.id,
      fieldLabel: field.labels[0],
      value: inferred.value,
      inferred: true,
      reason: inferred.reason,
    };
  }

  return null;
}

function chillicollectivoIsDictationTextControl(el) {
  if (!el) return false;
  const tag = String(el.tagName || '').toUpperCase();
  if (tag === 'TEXTAREA') return true;
  if (tag === 'INPUT') {
    const type = (el.type || 'text').toLowerCase();
    return ['text', 'search', 'email', 'url', ''].includes(type);
  }
  return false;
}

function chillicollectivoIsNeueInfoTextarea(el) {
  if (!chillicollectivoIsDictationTextControl(el)) return false;

  const className = String(el.className || '');
  if (/NoticeInput/i.test(className)) return true;

  const directMeta = [
    el.getAttribute('placeholder'),
    el.getAttribute('aria-label'),
    el.getAttribute('name'),
  ].filter(Boolean).join(' ').toLowerCase();
  if (/neue\s*info|new\s*info/.test(directMeta)) return true;

  const labelledBy = el.getAttribute('aria-labelledby');
  if (labelledBy) {
    const labelText = labelledBy.split(/\s+/)
      .map((id) => String(document.getElementById(id)?.textContent || '').replace(/\s+/g, ' ').trim())
      .filter(Boolean)
      .join(' ')
      .toLowerCase();
    if (/neue\s*info|new\s*info/.test(labelText)) return true;
  }

  let node = el.parentElement;
  for (let depth = 0; node && depth < 4; depth += 1, node = node.parentElement) {
    const t = String(node.textContent || '').replace(/\s+/g, ' ').trim().toLowerCase();
    if (t.length > 220) break;
    if (/neue\s*info|new\s*info/.test(t) && /hinzuf(ü|u)gen|\badd\b/.test(t)) return true;
  }

  const rect = el.getBoundingClientRect();
  const vw = window.innerWidth || document.documentElement.clientWidth || 1;
  const vh = window.innerHeight || document.documentElement.clientHeight || 1;
  const cx = rect.left + rect.width / 2;
  if (cx > vw * 0.2 && cx < vw * 0.8 && rect.top < vh * 0.42) return true;

  return false;
}

function chillicollectivoIsDialogInfoTextarea(el) {
  if (!el || el.tagName !== 'TEXTAREA') return false;
  if (chillicollectivoIsNeueInfoTextarea(el)) return false;

  if (String(el.className || '').includes('DialogInformation__TextInput')) return true;

  const directMeta = [
    el.getAttribute('placeholder'),
    el.getAttribute('aria-label'),
    el.getAttribute('name'),
  ].filter(Boolean).join(' ').toLowerCase();
  if (/nachricht|notiz|info|message|antwort|fake|kunde/.test(directMeta)) {
    const rect = el.getBoundingClientRect();
    const vh = window.innerHeight || document.documentElement.clientHeight || 1;
    if (rect.top < vh * 0.52) return true;
  }

  let node = el.parentElement;
  for (let depth = 0; node && depth < 5; depth += 1, node = node.parentElement) {
    const t = String(node.textContent || '').replace(/\s+/g, ' ').trim().toLowerCase();
    if (t.length > 280) break;
    if (/aktuelle\s*(kunden|nachrichten)|fake\s*nachrichten|kundennachrichten/.test(t)) return true;
  }

  return false;
}

function chillicollectivoIsProfileTextarea(el) {
  if (!el || el.tagName !== 'TEXTAREA') return false;
  if (chillicollectivoIsNeueInfoTextarea(el)) return false;
  if (chillicollectivoIsDialogInfoTextarea(el)) return false;
  if (el.closest?.('.th-pp-row, #kundePp, #fakePp')) return true;
  return false;
}

function chillicollectivoProfileSideOf(el) {
  const vw = window.innerWidth || document.documentElement.clientWidth || 1;
  const rect = el.getBoundingClientRect();
  const centerX = rect.left + rect.width / 2;
  if (centerX < vw * 0.35) return CHILLICOLLECTIVO_SIDE_CUSTOMER;
  if (centerX > vw * 0.65) return CHILLICOLLECTIVO_SIDE_FAKE;
  return null;
}

function chillicollectivoResolveFieldLabel(el) {
  const clean = t => (t || '').replace(/\s+/g, ' ').trim();

  const aria = clean(el.getAttribute('aria-label'));
  if (aria && aria !== '-') return aria;

  const labelledBy = el.getAttribute('aria-labelledby');
  if (labelledBy) {
    const parts = labelledBy.split(/\s+/).map(id => clean(document.getElementById(id)?.textContent)).filter(Boolean);
    if (parts.length) return parts.join(' ');
  }

  const row = el.closest('tr, li, div');
  if (row) {
    const candidates = [];
    for (const node of row.querySelectorAll('span, label, div, td, th, strong, p, b')) {
      if (node === el || el.contains(node) || node.contains(el)) continue;
      if (node.querySelector('textarea, input, [contenteditable="true"]')) continue;
      const text = clean(node.textContent);
      if (!text || text === '-' || text.length > 60) continue;
      if (text === clean(el.value)) continue;
      if (node.getBoundingClientRect().left >= el.getBoundingClientRect().left - 4) continue;
      candidates.push({ text, left: node.getBoundingClientRect().left });
    }
    candidates.sort((a, b) => a.left - b.left);
    if (candidates[0]) return candidates[0].text;
  }

  let prev = el.previousElementSibling;
  for (let i = 0; i < 5 && prev; i++, prev = prev.previousElementSibling) {
    const text = clean(prev.textContent);
    if (text && text !== '-' && text.length <= 60 && !prev.querySelector('textarea, input')) return text;
  }

  return '';
}

function chillicollectivoFieldLabelNorms(fieldDef) {
  if (!fieldDef?.labels?.length) return [];
  return fieldDef.labels.map(label => chillicollectivoNormalizeLabel(label));
}

function chillicollectivoFindProfileFieldById(side, fieldId) {
  const fieldDef = CHILLICOLLECTIVO_PROFILE_FIELDS.find((f) => f.id === fieldId);
  return fieldDef ? chillicollectivoFindProfileField(side, fieldDef) : null;
}

function chillicollectivoLabelMatchesField(labelText, fieldDef) {
  const norm = chillicollectivoNormalizeLabel(labelText);
  if (!norm) return false;
  return chillicollectivoFieldLabelNorms(fieldDef).includes(norm);
}

function chillicollectivoFindProfileField(side, fieldDef) {
  if (!chillicollectivoIsTorchwoodPage()) return null;

  let best = null;
  let bestTop = Infinity;

  for (const el of document.querySelectorAll('textarea')) {
    if (!chillicollectivoIsProfileTextarea(el)) continue;
    if (chillicollectivoProfileSideOf(el) !== side) continue;

    const label = chillicollectivoResolveFieldLabel(el);
    if (!chillicollectivoLabelMatchesField(label, fieldDef)) continue;

    const top = el.getBoundingClientRect().top;
    if (top < bestTop) {
      bestTop = top;
      best = el;
    }
  }

  return best;
}

function chillicollectivoSetNativeValue(el, value) {
  const proto = Object.getPrototypeOf(el);
  const ownDesc = Object.getOwnPropertyDescriptor(el, 'value');
  const protoDesc = proto ? Object.getOwnPropertyDescriptor(proto, 'value') : null;

  if (protoDesc?.set && (!ownDesc || ownDesc.set !== protoDesc.set)) {
    protoDesc.set.call(el, value);
  } else if (ownDesc?.set) {
    ownDesc.set.call(el, value);
  } else {
    el.value = value;
  }
}

function chillicollectivoGetProfileFieldValue(el) {
  if (!el) return '';
  return (el.value || el.textContent || '').replace(/\s+/g, ' ').trim();
}

function chillicollectivoGetPPLogCustomerContext() {
  const msg = typeof chillicollectivoGetLastCustomerMessage === 'function'
    ? chillicollectivoGetLastCustomerMessage()
    : '';
  return String(msg || '').trim() ? { hadCustomerMessage: true } : { hadCustomerMessage: false };
}

function chillicollectivoNormalizeSnippetMatchText(text) {
  return String(text || '')
    .toLowerCase()
    .replace(/ß/g, 'ss')
    .replace(/\s+/g, ' ')
    .trim();
}

function chillicollectivoFindCustomerSnippetForValue(customerMessage, extractedValue) {
  const msg = String(customerMessage || '').replace(/\s+/g, ' ').trim();
  const val = String(extractedValue || '').replace(/\s+/g, ' ').trim();
  if (!msg || !val) return { snippet: '', matched: false };

  const msgNorm = chillicollectivoNormalizeSnippetMatchText(msg);
  const valNorm = chillicollectivoNormalizeSnippetMatchText(val);
  if (!valNorm) return { snippet: msg.slice(0, 200), matched: false };

  const idx = msgNorm.indexOf(valNorm);
  if (idx >= 0) {
    const start = Math.max(0, idx - 50);
    const end = Math.min(msg.length, idx + val.length + 50);
    return { snippet: msg.slice(start, end).trim(), matched: true };
  }

  const words = valNorm.split(/\s+/).filter((w) => w.length >= 4);
  for (const word of words) {
    const widx = msgNorm.indexOf(word);
    if (widx >= 0) {
      const start = Math.max(0, widx - 50);
      const end = Math.min(msg.length, widx + word.length + 70);
      return { snippet: msg.slice(start, end).trim(), matched: true };
    }
  }

  return { snippet: msg.slice(0, 200), matched: false };
}

function chillicollectivoLogProfileCustomerToField(payload = {}) {
  const source = String(payload.source || '');
  if (source === 'pp-undo') return;

  const customerCtx = chillicollectivoGetPPLogCustomerContext();
  const customerMessage = typeof chillicollectivoGetLastCustomerMessage === 'function'
    ? String(chillicollectivoGetLastCustomerMessage() || '').trim()
    : '';
  const extractedValue = String(
    payload.extractedValue
    || payload.insertedSnippet
    || payload.dictationText
    || '',
  ).replace(/\s+/g, ' ').trim();
  if (!extractedValue) return;

  const fieldLabel = String(payload.fieldLabel || payload.fieldId || 'unbekannt');
  const sideLabel = String(payload.sideLabel || payload.side || '?');
  const flow = String(payload.flow || source || 'unknown');
  const extractionSource = String(payload.extractionSource || payload.reason || source || '');
  const match = chillicollectivoFindCustomerSnippetForValue(customerMessage, extractedValue);

  let message = '';
  if (flow === 'pp-auto-fill') {
    message = match.matched
      ? `Kundennachricht → PP ${sideLabel}/${fieldLabel}: «${extractedValue.slice(0, 80)}»`
      : `Profil-Text → PP ${sideLabel}/${fieldLabel}: «${extractedValue.slice(0, 80)}»`;
  } else if (flow === 'voice-command') {
    message = `Sprachbefehl → PP ${sideLabel}/${fieldLabel}: «${extractedValue.slice(0, 80)}»`;
  } else {
    message = `Diktat → PP ${sideLabel}/${fieldLabel}: «${extractedValue.slice(0, 80)}»`;
  }

  window.ChillicollectivoDiagnosticLog?.info?.('profile.customer-to-field', message, {
    ...customerCtx,
    route: payload.route || '',
    side: payload.side,
    sideLabel,
    fieldId: payload.fieldId,
    fieldLabel,
    extractedValue: extractedValue.slice(0, 400),
    extractedLen: extractedValue.length,
    matchedInCustomerMessage: match.matched,
    extractionSource,
    flow,
    valueBefore: String(payload.valueBefore || '').slice(0, 200),
    valueAfter: String(payload.valueAfter || '').slice(0, 200),
  }).catch?.(() => {});
}

function chillicollectivoLogProfileFieldInsert(payload = {}) {
  const el = payload.el;
  if (!el) return;
  const meta = chillicollectivoProfileFieldMetaFromElement(el);
  const fieldDef = CHILLICOLLECTIVO_PROFILE_FIELDS.find((f) => f.id === meta.fieldId);
  const fieldLabel = fieldDef?.labels?.[0] || meta.fieldId || 'unbekannt';
  const sideLabel = meta.side === CHILLICOLLECTIVO_SIDE_CUSTOMER
    ? 'Kunde'
    : meta.side === CHILLICOLLECTIVO_SIDE_FAKE
      ? 'Fake'
      : (meta.side || '?');
  const customerCtx = chillicollectivoGetPPLogCustomerContext();
  const fm = globalThis.__chillicollectivoFieldManager;
  const route = typeof fm?._currentConversationKey === 'function' ? fm._currentConversationKey() : '';

  const before = String(payload.valueBefore ?? '').trim();
  const after = String(payload.valueAfter ?? '').trim();
  let insertedSnippet = String(payload.insertedText ?? payload.dictationText ?? '').trim();
  if (!insertedSnippet && after !== before) {
    if (payload.append && before && after.startsWith(before)) {
      insertedSnippet = after.slice(before.length).trim();
    } else if (!before) {
      insertedSnippet = after;
    } else {
      insertedSnippet = after;
    }
  }

  window.ChillicollectivoDiagnosticLog?.info?.('profile.insert', `PP ${sideLabel}/${fieldLabel}: eingetragen`, {
    ...customerCtx,
    route,
    side: meta.side,
    sideLabel,
    fieldId: meta.fieldId,
    fieldLabel,
    valueBefore: before.slice(0, 400),
    valueAfter: after.slice(0, 400),
    insertedSnippet: insertedSnippet.slice(0, 400),
    insertedLen: insertedSnippet.length,
    markedText: String(payload.markedText || '').slice(0, 200),
    dictationText: String(payload.dictationText || '').slice(0, 200),
    append: !!payload.append,
    source: String(payload.source || 'unknown'),
    reason: String(payload.reason || ''),
    hadCustomerMessage: customerCtx.hadCustomerMessage === true,
  }).catch?.(() => {});

  chillicollectivoLogProfileCustomerToField({
    route,
    side: meta.side,
    sideLabel,
    fieldId: meta.fieldId,
    fieldLabel,
    valueBefore: before,
    valueAfter: after,
    insertedSnippet,
    dictationText: String(payload.dictationText || '').trim(),
    extractedValue: insertedSnippet,
    extractionSource: String(payload.extractionSource || payload.reason || payload.source || ''),
    flow: String(payload.flow || payload.source || 'unknown'),
    profileTextSnippet: String(payload.profileTextSnippet || ''),
    source: String(payload.source || 'unknown'),
  });
}

function chillicollectivoWriteProfileField(el, value, append = false, meta = {}) {
  if (!el) return false;

  const valueBefore = chillicollectivoGetProfileFieldValue(el);
  el.focus?.();
  const cleanValue = chillicollectivoStripEmojisFromProfileText(value);
  const next = append
    ? `${(el.value || '').trim()}${el.value && !/\s$/.test(el.value) ? ' ' : ''}${cleanValue}`.trim()
    : cleanValue;

  if (!next && !append) {
    chillicollectivoSetNativeValue(el, '');
    el.dispatchEvent(new InputEvent('input', { bubbles: true, inputType: 'insertText', data: null }));
    el.dispatchEvent(new Event('change', { bubbles: true }));
    if (valueBefore) {
      chillicollectivoLogProfileFieldInsert({
        el,
        valueBefore,
        valueAfter: '',
        append,
        ...meta,
      });
    }
    return true;
  }
  if (!next) return false;

  chillicollectivoSetNativeValue(el, next);

  const pos = next.length;
  try {
    el.setSelectionRange(pos, pos);
  } catch (_) {}

  el.dispatchEvent(new InputEvent('input', { bubbles: true, inputType: 'insertText', data: null }));
  el.dispatchEvent(new Event('change', { bubbles: true }));

  if (next !== valueBefore || meta.forceLog) {
    chillicollectivoLogProfileFieldInsert({
      el,
      valueBefore,
      valueAfter: next,
      append,
      ...meta,
    });
  }

  requestAnimationFrame(() => {
    try {
      if (document.contains(el)) {
        el.focus?.({ preventScroll: true });
        el.setSelectionRange?.(pos, pos);
      }
    } catch (_) {}
  });

  return true;
}

function chillicollectivoIsProfileFieldEmpty(value) {
  const v = (value || '').replace(/\s+/g, ' ').trim();
  return !v || v === '-';
}

function chillicollectivoGetProfileFieldValue(el) {
  if (!el) return '';
  return (el.value || el.textContent || '').replace(/\s+/g, ' ').trim();
}

function chillicollectivoFindSideProfileRoot(side) {
  if (!chillicollectivoIsTorchwoodPage()) return null;

  const vw = window.innerWidth || document.documentElement.clientWidth || 1;
  const targetX = side === CHILLICOLLECTIVO_SIDE_CUSTOMER ? vw * 0.17 : vw * 0.83;
  let best = null;
  let bestScore = Infinity;

  for (const el of document.querySelectorAll('div, section, aside, main')) {
    const rect = el.getBoundingClientRect();
    if (rect.width < 120 || rect.height < 280) continue;

    const centerX = rect.left + rect.width / 2;
    if (side === CHILLICOLLECTIVO_SIDE_CUSTOMER && centerX > vw * 0.42) continue;
    if (side === CHILLICOLLECTIVO_SIDE_FAKE && centerX < vw * 0.58) continue;

    const text = (el.textContent || '').replace(/\s+/g, ' ');
    if (!/profil informationen/i.test(text) || !/globale informationen/i.test(text)) continue;

    const score = Math.abs(centerX - targetX) + Math.max(rect.width - 360, 0) * 0.15;
    if (score < bestScore) {
      bestScore = score;
      best = el;
    }
  }

  return best;
}

function chillicollectivoCompactNodeText(el) {
  if (!el) return '';
  const direct = Array.from(el.childNodes)
    .filter(n => n.nodeType === Node.TEXT_NODE)
    .map(n => n.textContent)
    .join('')
    .replace(/\s+/g, ' ')
    .trim();
  if (direct) return direct;
  const full = (el.textContent || '').replace(/\s+/g, ' ').trim();
  return full.length <= 80 ? full : '';
}

function chillicollectivoApplyMuppetToField(el) {
  try {
    window.__chillicollectivoMuppet?.applyToField?.(el);
  } catch (e) {
    console.warn('[CHILLICOLLECTIVO] Muppet:', e);
  }
}

function chillicollectivoFindProfilInfoScope(root) {
  if (!root) return null;

  const harborProfil = root.querySelector('#kundeProfilInfo, #fakeProfilInfo');
  if (harborProfil) return harborProfil;

  const profilHeader = [...root.querySelectorAll('*')].find(n =>
    /^profil informationen$/i.test(chillicollectivoCompactNodeText(n))
  );
  if (!profilHeader) return null;

  const block = profilHeader.closest('.th-block') || profilHeader.parentElement;
  const globalInBlock = block && [...block.querySelectorAll('*')].find(n =>
    /^globale informationen$/i.test(chillicollectivoCompactNodeText(n))
  );

  if (block && !globalInBlock) return block;

  if (block && globalInBlock) {
    const wrapper = document.createElement('div');
    wrapper.setAttribute('data-chillicollectivo-profil-scope', '1');
    let node = profilHeader.nextElementSibling;
    while (node && node !== globalInBlock) {
      wrapper.appendChild(node.cloneNode(true));
      node = node.nextElementSibling;
    }
    if (wrapper.childNodes.length) return wrapper;
  }

  return profilHeader.parentElement;
}

function chillicollectivoReadProfilBeschreibung(profilScope, fields) {
  const { ort, beziehung, interessen, sucheNach } = fields;

  const descEl = profilScope?.querySelector?.('.th-desc');
  if (descEl) {
    const t = (descEl.textContent || '').replace(/\s+/g, ' ').trim();
    if (t.length >= 3) return t;
  }

  const stopNorms = new Set([
    'ort', 'beziehung', 'interessen', 'ich suche nach', 'profil informationen',
    'globale informationen', 'kontaktdaten erhalten', 'name', 'körper', 'koerper',
    'geburtstag', 'beruf',
  ].map(chillicollectivoNormalizeLabel));

  let beschreibung = '';
  let bestLen = 0;

  for (const el of profilScope.querySelectorAll('div, span, p')) {
    if (el.closest('.th-kv, .th-pp-row')) continue;
    if (el.matches('.th-kv, .th-label, .th-val, .th-block-title, .th-pp-label')) continue;
    if (el.querySelector('textarea, input, .th-kv')) continue;

    const rect = el.getBoundingClientRect();
    if (rect.height <= 0 || rect.width <= 0) continue;

    const text = (el.textContent || '').replace(/\s+/g, ' ').trim();
    if (!text || text.length < 4 || text.length > 280) continue;

    const norm = chillicollectivoNormalizeLabel(text);
    if (stopNorms.has(norm)) continue;
    if (/^(ort|beziehung|interessen|ich suche nach)/i.test(text.replace(/\s+/g, ''))) continue;
    if (/profil informationen|globale informationen|haarfarbe|größe|groesse|raucher/i.test(text)) continue;
    if ([ort, beziehung, interessen, sucheNach].includes(text)) continue;
    if (interessen && norm.includes(chillicollectivoNormalizeLabel(interessen)) && /^interessen/i.test(text.replace(/\s+/g, ''))) continue;

    const compact = chillicollectivoCompactNodeText(el);
    if (!compact || compact.length < 4) continue;
    if ([ort, beziehung, interessen, sucheNach].filter(Boolean).includes(compact)) continue;

    if (compact.length >= bestLen) {
      bestLen = compact.length;
      beschreibung = compact;
    }
  }

  return beschreibung;
}

function chillicollectivoIsUsableBeschreibung(desc, info) {
  if (!desc || desc.length < 3) return false;
  const norm = chillicollectivoNormalizeLabel(desc);
  if (/^(interessen|beziehung|ich suche nach|ort)/.test(norm)) return false;

  for (const val of [info.interessen, info.beziehung, info.sucheNach, info.ort]) {
    if (!val) continue;
    const nv = chillicollectivoNormalizeLabel(val);
    if (norm === nv || (nv.length > 8 && norm.includes(nv))) return false;
  }

  return true;
}

function chillicollectivoFindLabelValueInScope(scope, label) {
  const want = chillicollectivoNormalizeLabel(label);
  if (!scope || !want) return '';

  for (const el of scope.querySelectorAll('span, div, td, th, label, p, strong, b')) {
    const labelText = chillicollectivoCompactNodeText(el);
    if (!labelText || chillicollectivoNormalizeLabel(labelText) !== want) continue;
    if (el.querySelector('textarea, input, [contenteditable="true"]')) continue;

    const row = el.closest('tr, li, div');
    if (row) {
      const cells = row.querySelectorAll(':scope > *');
      let seenLabel = false;
      for (const cell of cells) {
        if (cell === el || cell.contains(el)) {
          seenLabel = true;
          continue;
        }
        if (!seenLabel) continue;
        const val = chillicollectivoCompactNodeText(cell);
        if (val && val !== '-' && chillicollectivoNormalizeLabel(val) !== want) return val;
      }
    }

    let sib = el.nextElementSibling;
    for (let i = 0; i < 4 && sib; i++, sib = sib.nextElementSibling) {
      const val = chillicollectivoCompactNodeText(sib);
      if (val && val !== '-' && chillicollectivoNormalizeLabel(val) !== want) return val;
    }
  }

  return '';
}

function chillicollectivoReadProfilInformationen(side) {
  const root = chillicollectivoFindSideProfileRoot(side);
  if (!root) return { root: null, name: '', alter: '', geburtstag: '', ort: '', beziehung: '', interessen: '', sucheNach: '', beschreibung: '' };

  const profilScope = chillicollectivoFindProfilInfoScope(root) || root;

  const ort = chillicollectivoFindLabelValueInScope(profilScope, 'Ort');
  const beziehung = chillicollectivoFindLabelValueInScope(profilScope, 'Beziehung');
  const interessen = chillicollectivoFindLabelValueInScope(profilScope, 'Interessen');
  const sucheNach = chillicollectivoFindLabelValueInScope(profilScope, 'Ich suche nach');
  const name = chillicollectivoFindLabelValueInScope(profilScope, 'Name');
  const alter = chillicollectivoFindLabelValueInScope(profilScope, 'Alter');
  const geburtstag = chillicollectivoFindLabelValueInScope(profilScope, 'Geburtstag');
  const beschreibung = chillicollectivoReadProfilBeschreibung(profilScope, { ort, beziehung, interessen, sucheNach });

  return { root, name, alter, geburtstag, ort, beziehung, interessen, sucheNach, beschreibung };
}

function chillicollectivoCombinedProfilText(info) {
  return [info.name, info.beschreibung, info.interessen, info.beziehung, info.ort, info.alter]
    .filter(Boolean)
    .join(' · ')
    .replace(/\s+/g, ' ')
    .trim();
}

function chillicollectivoExtractSnippet(text, pattern) {
  const m = (text || '').match(pattern);
  return m ? (m[1] || m[0]).replace(/\s+/g, ' ').trim() : '';
}

function chillicollectivoNormalizeNameToken(value) {
  return String(value || '')
    .toLowerCase()
    .replace(/ä/g, 'ae')
    .replace(/ö/g, 'oe')
    .replace(/ü/g, 'ue')
    .replace(/ß/g, 'ss');
}

/** Wörter nach «ich bin …», die kein Vorname sind (Status, Eigenschaft, Beruf). */
const CHILLICOLLECTIVO_ICH_BIN_NOT_NAMES = new Set([
  'verheiratet', 'geschieden', 'getrennt', 'ledig', 'single', 'verwitwet', 'verlobt',
  'gross', 'klein', 'jung', 'alt', 'duenn', 'dick', 'schlank', 'mollig',
  'gluecklich', 'glucklich', 'traurig', 'muede', 'krank', 'gesund', 'nervoes', 'nervos',
  'offen', 'ehrlich', 'treu', 'unsicher', 'sicher', 'stolz', 'froh', 'verliebt',
  'raucher', 'raucherin', 'nichraucher', 'nichraucherin',
  'maennlich', 'weiblich', 'hetero', 'schwul', 'lesbisch', 'bisexuell',
  'deutsch', 'oesterreicher', 'schweizer', 'neu', 'bereit', 'motiviert', 'interessiert',
  'selbststaendig', 'selbstaendig', 'arbeitlos', 'rentner', 'rentnerin', 'student', 'studentin',
  'lehrer', 'lehrerin', 'arzt', 'aerztin', 'ingenieur', 'ingenieurin', 'verkaeufer', 'verkaeuferin',
  'sehr', 'total', 'echt', 'wirklich', 'absolut', 'ziemlich',
]);

function chillicollectivoIsProfileTemplateGarbage(value) {
  const v = String(value || '').replace(/\s+/g, ' ').trim();
  if (!v) return true;
  if (/^ich suche nach[-–]/i.test(v) && /größe|groesse|haarfarbe|raucher/i.test(v)) return true;
  if (/größe[-–]haarfarbe|groesse[-–]haarfarbe|haarfarbe[-–]raucher/i.test(v)) return true;
  if ((v.match(/[-–]/g) || []).length >= 3 && v.length < 90) return true;
  return false;
}

function chillicollectivoIsExplicitChatDump(value) {
  const v = String(value || '').replace(/\s+/g, ' ').trim().toLowerCase();
  if (v.length < 40) return false;
  const hits = (v.match(/\b(anal|blasen|lecken|reiten|doggy|69er|rollenspiel|dreier|bdsm|hardcore)\b/g) || []).length;
  return hits >= 3;
}

function chillicollectivoExtractNameFromBioText(text) {
  const re = /\bich bin ([A-ZÄÖÜ][a-zäöüß]{1,30})\b/gi;
  let match;
  while ((match = re.exec(text || '')) !== null) {
    const candidate = String(match[1] || '').trim();
    if (!candidate) continue;
    if (CHILLICOLLECTIVO_ICH_BIN_NOT_NAMES.has(chillicollectivoNormalizeNameToken(candidate))) continue;
    return candidate;
  }
  return '';
}

function chillicollectivoExtractTattooPiercing(text) {
  if (!/tattoo|piercing|tätow/i.test(text || '')) return '';
  if (/kein(?:e|en|es)?\s+tattoo|ohne\s+tattoo|kein\s+piercing|nein[^.!?]{0,30}tattoo/i.test(text)) return 'Nein';
  return chillicollectivoExtractSnippet(text, /(tattoo[^,.!?]{0,50}|piercing[^,.!?]{0,50}|tätowier[^,.!?]{0,50})/i) || 'Ja';
}

function chillicollectivoCollectVorliebenKeywords(source) {
  const hits = [];
  const seen = new Set();
  const text = String(source || '');
  const rules = [
    [/\b69(?:er)?\b/i, '69'],
    [/\bdessous\b/i, 'Dessous'],
    [/\bblowjob\b/i, 'Blowjob'],
    [/\bexperimentierfreudig\b/i, 'Experimentierfreudig'],
    [/\bk(?:ue|ü)(?:ss|s)(?:en)?\b/i, 'Küssen'],
    [/\bdoggy(?:\s*style)?\b/i, 'Doggy'],
    [/\breiter(?:stellung)?\b|\breiten\b/i, 'Reiterstellung'],
    [/sex\s+im\s+freien\b/i, 'Sex im Freien'],
    [/\brollenspiel(?:e)?\b/i, 'Rollenspiele'],
    [/\bromantisch(?:en?\s+sex|er?\s+sex)\b/i, 'Romantischer Sex'],
    [/\bromantisch\b/i, 'Romantischer Sex'],
    [/\boral\b/i, 'Oral'],
    [/\boffen\s+f(?:ue|ü)r\s+neues\b/i, 'Offen für Neues'],
    [/ohne\s+gummi|ohne\s+kondom|ohne\s+gummis|unge?sch(?:ue|u)tzt/i, 'ohne gummi'],
    [/rein\s*spritzen|in\s+(?:dich|mir|sie|ihn)\s*rein/i, 'ohne gummi'],
    [/\bmag(?:e|en)?\s+(?:im\s+(?:grunde?|prinzip|wesentlichen)|eigentlich|so\s+ziemlich|fast|ziemlich)\s+alles\b|\bmag(?:e|en)?\s+vieles\b/i, 'Mag vieles'],
    [/\bfingern\b|\brumfingern\b|\bhartfingern\b/i, 'Fingern'],
    [/\bleck(?:en|e|st|t)\b|\brumleck(?:en|e|st|t)\b|\bausleck(?:en|e|st|t)\b/i, 'Lecken'],
    [/\bhohe\s+schuh(?:e|en)?\b|\bschuh(?:e|en)?\b/i, 'Schuhe'],
    [/\bkleid(?:er|chen)?\b/i, 'Kleider'],
    [/\b(?:rock|röcke)\b/i, 'Rock'],
    [/\boutdoor\b/i, 'Outdoor'],
  ];
  for (const [pattern, value] of rules) {
    if (!pattern.test(text)) continue;
    const key = value.toLowerCase();
    if (seen.has(key)) continue;
    seen.add(key);
    hits.push(value);
  }
  return hits;
}

function chillicollectivoExtractVorlieben(text) {
  const hits = chillicollectivoCollectVorliebenKeywords(text);
  if (hits.length) return hits.join(', ');
  const legacy = [];
  if (/\bbdsm\b/i.test(text || '')) legacy.push('BDSM');
  if (/hardcore/i.test(text || '')) legacy.push('Hardcore');
  if (/blümchensex/i.test(text || '')) legacy.push('Blümchensex');
  if (/softcore/i.test(text || '')) legacy.push('Softcore');
  if (/extrovertiert/i.test(text || '')) legacy.push('Extrovertiert');
  if (/introvertiert/i.test(text || '')) legacy.push('Introvertiert');
  if (/\bwild\b/i.test(text || '')) legacy.push('wild');
  if (/charmant/i.test(text || '')) legacy.push('charmant');
  if (/romantisch/i.test(text || '')) legacy.push('romantisch');
  return legacy.join(', ');
}

function chillicollectivoParseGermanCountToken(token) {
  const raw = String(token || '').trim().toLowerCase();
  if (/^\d+$/.test(raw)) return parseInt(raw, 10);
  const map = {
    ein: 1, eine: 1, einer: 1, eins: 1, eim: 1,
    zwei: 2, zwo: 2, drei: 3, vier: 4, fuenf: 5, fünf: 5,
    sechs: 6, sieben: 7, acht: 8, neun: 9, zehn: 10,
  };
  return map[raw] ?? null;
}

function chillicollectivoParseGermanYearsToken(token) {
  const raw = String(token || '').trim().toLowerCase().replace(/\s+/g, '');
  if (/^\d+[.,]\d+$/.test(raw)) {
    const num = parseFloat(raw.replace(',', '.'));
    return Number.isFinite(num) && num > 0 ? num : null;
  }
  return chillicollectivoParseGermanCountToken(token);
}

function chillicollectivoFormatYearsLabel(years) {
  if (years == null || years <= 0) return '';
  if (Number.isInteger(years)) return String(years);
  return String(years).replace('.', ',');
}

function chillicollectivoFormatJahreSingleLabel(years) {
  const label = chillicollectivoFormatYearsLabel(years);
  return label ? `${label} Jahre single` : '';
}

function chillicollectivoCustomerImpliesSingleStatus(text) {
  const source = String(text || '');
  return /\bsingle\b/i.test(source)
    || /suche?\s+(?:auch\s+)?(?:etwas|was)\s+neu(?:es|em)?/i.test(source)
    || /(?:auf der )?suche nach/i.test(source)
    || /letzte\s+beziehung\s+(?:ist|war)\s+/i.test(source);
}

function chillicollectivoExtractCustomerRelationshipFacts(text) {
  const facts = [];
  const source = String(text || '').replace(/\s+/g, ' ').trim();
  if (!source) return facts;

  const countPattern = '(\\d+(?:[.,]\\d+)?|\\d{1,2}|ein|eine|einer|eins|zwei|zwo|drei|vier|fuenf|fünf|sechs|sieben|acht|neun|zehn)';
  const modifier = '(?:knapp|fast|ca\\.?|circa|etwa)\\s+';
  const letzteParts = [];
  const seenLetzte = new Set();

  const pushLetzte = (value, reason) => {
    const v = String(value || '').trim();
    if (!v || seenLetzte.has(v.toLowerCase())) return;
    seenLetzte.add(v.toLowerCase());
    letzteParts.push(v);
  };

  const letzteBeziehungHerMatch = source.match(new RegExp(
    `(?:meine\\s+)?letzte\\s+beziehung\\s+(?:ist|war)\\s+(?:schon|bereits|jetzt)?\\s*(?:vor\\s+)?${modifier}?${countPattern}\\s+jahr(?:e)?\\s+her`,
    'i',
  ));
  if (letzteBeziehungHerMatch) {
    const years = chillicollectivoParseGermanYearsToken(letzteBeziehungHerMatch[1]);
    if (years) {
      pushLetzte(chillicollectivoFormatJahreSingleLabel(years), 'customer-relationship-ended');
    }
  }

  const singleMatch = source.match(new RegExp(`${modifier}?${countPattern}\\s+jahr(?:e)?\\s+single`, 'i'));
  if (singleMatch) {
    const years = chillicollectivoParseGermanYearsToken(singleMatch[1]);
    if (years) pushLetzte(chillicollectivoFormatJahreSingleLabel(years), 'customer-years-single');
  } else if (/\bsingle\b/i.test(source) && !letzteBeziehungHerMatch) {
    pushLetzte('Single', 'customer-single');
  } else if (chillicollectivoCustomerImpliesSingleStatus(source) && !letzteBeziehungHerMatch) {
    const seitMatch = source.match(new RegExp(`(?:seit|schon|bereits)\\s+${modifier}?${countPattern}\\s+jahr(?:e)?`, 'i'));
    if (seitMatch) {
      const years = chillicollectivoParseGermanYearsToken(seitMatch[1]);
      if (years) pushLetzte(chillicollectivoFormatJahreSingleLabel(years), 'customer-implied-single');
    }
  }

  const ohneSexPatterns = [
    new RegExp(`letzte(?:s)?\\s+mal\\s+(?:schon\\s+|bereits\\s+)?(?:ü\\s*nen|einen|ein|eine|einer|eins)\\s+${modifier}?${countPattern}\\s+jahr(?:e)?`, 'i'),
    new RegExp(`${modifier}?${countPattern}\\s+jahr(?:e)?\\s+(?:schon\\s+|bereits\\s+)?ohne\\s+sex`, 'i'),
  ];
  for (const pattern of ohneSexPatterns) {
    const match = source.match(pattern);
    if (!match) continue;
    const years = chillicollectivoParseGermanYearsToken(match[1]);
    if (!years) continue;
    const ohneSexLabel = years === 1 ? '1 Jahr ohne Sex' : `${chillicollectivoFormatYearsLabel(years)} Jahre ohne Sex`;
    facts.push({ fieldId: 'sonstiges', value: ohneSexLabel, reason: 'customer-without-sex' });
    if (!letzteBeziehungHerMatch) pushLetzte(ohneSexLabel, 'customer-without-sex-letzte');
    break;
  }

  if (letzteParts.length) {
    facts.push({
      fieldId: 'letzte_beziehung',
      value: letzteParts.join(', '),
      reason: letzteParts.length > 1 ? 'customer-relationship-combined' : 'customer-relationship',
    });
  }

  return facts;
}

function chillicollectivoExtractNoGos(text) {
  if (!/(?:auf[s]?\s+klo|klo\s+sachen|toilette|toilettenhumor)/i.test(text || '')) return '';
  if (/geht(?:\s+für\s+mich)?\s+gar?nicht/i.test(text) && /klo/i.test(text)) return 'Klo-Sachen';
  return 'Klo-Sachen';
}

function chillicollectivoExtractKoerper(text) {
  const parts = [];
  if (/\b(?:eher\s+)?klein\b/i.test(text || '')) parts.push('klein');
  else if (/\b(?:eher\s+)?(?:groß|gross)\b/i.test(text || '')) parts.push('groß');
  if (/\bich rauch|\brauche?\b|\braucher\b/i.test(text || '')) parts.push('Raucher');
  return parts.join(', ');
}

function chillicollectivoExtractBeruf(text) {
  const t = text || '';
  if (/wollte\s+(?:früher\s+)?(?:immer\s+)?(?:mal\s+)?(?:ein(?:en)?\s+)?(?:werden|sein)/i.test(t)) return '';
  if (/träum(?:te|en)\s+(?:davon|von)/i.test(t)) return '';
  if (/wochenende\s+frei|samstag\s+frei|sonntag\s+frei/i.test(t) && !/(?:als|bei|von beruf|arbeite)/i.test(t)) return '';
  const real = chillicollectivoExtractSnippet(t, /(?:ich\s+)?(?:bin|arbeite)\s+(?:als|bei)\s+[^.!?]{0,40}/i)
    || chillicollectivoExtractSnippet(t, /von beruf[^.!?]{0,40}/i);
  return real;
}

function chillicollectivoExtractKinder(text) {
  const t = text || '';
  if (/keine?\s+kinder|ohne\s+kinder|habe keine kinder/i.test(t)) return 'Keine';
  if (/\bsohn\b/i.test(t)) {
    const parts = ['Sohn'];
    if (/\bmai\b/i.test(t) && /geburtstag|sohn/i.test(t)) parts.push('Mai Geburtstag');
    return parts.join(', ');
  }
  if (/\btochter\b/i.test(t)) return 'Tochter';
  return '';
}

function chillicollectivoExtractLetzteBeziehung(text) {
  const t = text || '';
  if (/trauerspiel/i.test(t)) return 'Traurerspiel';
  const seit = t.match(/seit \d+ jahr(?:en)? (?:single|getrennt|geschieden)/i);
  if (seit) return seit[0];
  const kurz = chillicollectivoExtractSnippet(t, /letzte beziehung war (?:ein )?[^.!?]{0,30}/i);
  if (kurz) return kurz.replace(/^letzte beziehung war (?:ein )?/i, '').trim();
  const status = t.match(/\b(?:single|geschieden|verheiratet|getrennt|ledig|verwitwet)\b/i);
  if (status) {
    const w = status[0];
    return w.charAt(0).toUpperCase() + w.slice(1).toLowerCase();
  }
  return '';
}

const CHILLICOLLECTIVO_PP_MANUAL_ONLY_FIELDS = new Set(['sonstiges']);
const CHILLICOLLECTIVO_PP_AUTO_EXCLUDED_FIELDS = CHILLICOLLECTIVO_PP_MANUAL_ONLY_FIELDS;

function chillicollectivoBuildPPAutoSuggestions(info, side) {
  const suggestions = [];
  const seen = new Set();
  const text = chillicollectivoCombinedProfilText(info);
  const desc = chillicollectivoIsUsableBeschreibung(info.beschreibung || '', info) ? info.beschreibung : '';

  function push(fieldId, value, source) {
    if (CHILLICOLLECTIVO_PP_MANUAL_ONLY_FIELDS.has(fieldId)) return;
    const v = chillicollectivoStripEmojisFromProfileText((value || '').replace(/\s+/g, ' ').trim());
    if (!v || seen.has(fieldId)) return;
    if (chillicollectivoIsProfileTemplateGarbage(v)) return;
    if (fieldId === 'vorlieben' && chillicollectivoIsExplicitChatDump(v)) return;
    if (fieldId === 'hobbys' && chillicollectivoIsProfileTemplateGarbage(v)) return;
    seen.add(fieldId);
    suggestions.push({ fieldId, value: v, source });
  }

  function resolveSuchtNach() {
    if (/f\s*\+|freundschaft\s*plus|freundschaft\s*\+/i.test(text)) {
      return 'Freundschaft plus';
    }
    if (/beziehung/i.test(desc) && /f\s*\+|plus|freundschaft/i.test(desc)) {
      return 'Freundschaft plus';
    }
    if (/verabredung/i.test(text)) {
      return chillicollectivoExtractSnippet(text, /schöne verabredung/i) ? 'Verabredung' : (info.sucheNach || 'Verabredung');
    }
    if (/find(?:et|s)?\s+(?:was\s+)?raus/i.test(text)) return 'Finds raus';
    if (/one\s*night\s*stand|\bons\b/i.test(text)) return 'One Night Stand';
    if (/fest(?:e)?\s+beziehung|ernsthaft/i.test(text)) return 'Feste Beziehung';
    if (/locker[^.!?]{0,40}fest|fest[^.!?]{0,40}locker/i.test(text)) {
      return 'Offen (locker & fest)';
    }
    return info.sucheNach || '';
  }

  function isStructuredFactBio(value) {
    if (!value) return false;
    if (value.split(',').length >= 2 && /tattoo|piercing|f\s*\+|freundschaft\s*\+|hund namens|hamster namens|blümchensex|bdsm|mpc|rauch|sohn|tochter|lieblingsdrink/i.test(value)) {
      return true;
    }
    return false;
  }

  if (info.name) push('name', info.name, 'Profil');
  if (side === CHILLICOLLECTIVO_SIDE_FAKE && info.geburtstag) push('geburtstag', info.geburtstag, 'Profil');

  const nameFromText = chillicollectivoExtractNameFromBioText(text);
  if (nameFromText && !seen.has('name')) push('name', nameFromText, 'Beschreibung');

  if (info.interessen) push('hobbys', info.interessen, 'Interessen');
  if (info.beziehung) push('letzte_beziehung', info.beziehung, 'Beziehung');

  const suchtNachVal = resolveSuchtNach();
  if (suchtNachVal) push('sucht_nach', suchtNachVal, 'Profil/Bio');

  const letzteBez = chillicollectivoExtractLetzteBeziehung(text);
  if (letzteBez) push('letzte_beziehung', letzteBez, 'Text');

  if (/geschieden|verheiratet|getrennt|ledig|single/i.test(text) && !seen.has('letzte_beziehung')) {
    const m = text.match(/seit \d+ jahr(?:en)? (?:single|getrennt|geschieden)|geschieden|verheiratet|getrennt|ledig|single/i);
    if (m) push('letzte_beziehung', m[0], 'Text');
  }

  const kinderVal = chillicollectivoExtractKinder(text);
  if (kinderVal) push('kinder', kinderVal, 'Text');

  const haustier = chillicollectivoExtractSnippet(text, /(?:hamster|hund|katze|haustier)\s+namens\s+[A-Za-zÄÖÜäöüß]+/i)
    || chillicollectivoExtractSnippet(text, /(?:hamster|hund|katze|haustier)[^,.!?]{0,40}/i);
  if (haustier) push('haustiere', haustier, 'Schlüsselwort');

  const tattooVal = chillicollectivoExtractTattooPiercing(text);
  if (tattooVal) push('tattoo_piercing', tattooVal, 'Schlüsselwort');

  const noGos = chillicollectivoExtractNoGos(text);
  if (noGos) push('no_gos', noGos, 'Schlüsselwort');

  const essenTrinken = chillicollectivoExtractSnippet(text, /lieblingsdrinks[^.!?]{0,80}/i)
    || chillicollectivoExtractSnippet(text, /(?:cola|whiskey|wasser)[^.!?]{0,60}/i);
  if (essenTrinken) push('essen_trinken', essenTrinken, 'Text');

  const beruf = chillicollectivoExtractBeruf(text);
  if (beruf) push('beruf', beruf, 'Text');

  const koerper = chillicollectivoExtractKoerper(text);
  if (koerper) push('koerper', koerper, 'Text');

  const vorlieben = chillicollectivoExtractVorlieben(text);
  if (vorlieben) {
    push('vorlieben', vorlieben, 'Schlüsselwort');
  } else if (desc && !isStructuredFactBio(desc) && desc.length <= 90 && /extrovertiert|introvertiert|wild|charmant|humor|ehrlich|offen|sportlich|romantisch/i.test(desc)) {
    push('vorlieben', desc, 'Beschreibung');
  }

  return suggestions;
}

const CHILLICOLLECTIVO_PP_UNDO_MAX = 15;

function chillicollectivoGetPPUndoStack() {
  if (!window.__chillicollectivoPPUndoStack) window.__chillicollectivoPPUndoStack = [];
  return window.__chillicollectivoPPUndoStack;
}

function chillicollectivoCapturePPSideState(side) {
  const fields = [];
  for (const fieldDef of CHILLICOLLECTIVO_PROFILE_FIELDS) {
    const el = chillicollectivoFindProfileField(side, fieldDef);
    fields.push({
      fieldId: fieldDef.id,
      value: el ? chillicollectivoGetProfileFieldValue(el) : '',
      el,
    });
  }
  return { side, fields };
}

function chillicollectivoPushPPUndoEntry(sideStates) {
  if (!sideStates?.length) return;
  const stack = chillicollectivoGetPPUndoStack();
  stack.push({ ts: Date.now(), sides: sideStates });
  while (stack.length > CHILLICOLLECTIVO_PP_UNDO_MAX) stack.shift();
}

function chillicollectivoRestorePPUndoEntry(entry, sideFilter = null) {
  let restored = 0;
  for (const sideState of entry.sides) {
    if (sideFilter && sideState.side !== sideFilter) continue;
    for (const f of sideState.fields) {
      let el = f.el;
      if (!el || !document.contains(el)) {
        const fieldDef = CHILLICOLLECTIVO_PROFILE_FIELDS.find(x => x.id === f.fieldId);
        el = fieldDef ? chillicollectivoFindProfileField(sideState.side, fieldDef) : null;
      }
      if (!el) continue;
      chillicollectivoWriteProfileField(el, f.value, false, { source: 'pp-undo' });
      restored++;
    }
  }
  return restored;
}

function chillicollectivoUndoLastPP(sideFilter = null) {
  const stack = chillicollectivoGetPPUndoStack();
  if (!stack.length) return { ok: false, restored: 0, reason: 'empty' };

  const entry = stack.pop();
  const sidesToRestore = sideFilter
    ? entry.sides.filter(s => s.side === sideFilter)
    : entry.sides;

  if (sideFilter && !sidesToRestore.length) {
    stack.push(entry);
    return { ok: false, restored: 0, reason: 'no-match' };
  }

  const restored = chillicollectivoRestorePPUndoEntry(entry, sideFilter);
  return { ok: restored > 0, restored, entry, sideFilter };
}

function chillicollectivoNormalizeUndoCommandText(text) {
  return chillicollectivoNormalizePPAutoCommandText(text)
    .replace(/rückgängig/g, 'rueckgaengig')
    .replace(/rückgangig/g, 'rueckgaengig')
    .replace(/zurück/g, 'zurueck')
    .replace(/widerrufen/g, 'rueckgaengig');
}

function chillicollectivoParsePPUndoCommand(text) {
  const n = chillicollectivoNormalizeUndoCommandText(text);
  if (!n) return null;

  const kw = chillicollectivoGetSideKeywords();
  const pp = chillicollectivoEscapeRegex(chillicollectivoGetProfileCmdConfig().ppKeyword);
  const undoAlt = chillicollectivoGetUndoTokens().map(chillicollectivoEscapeRegex).join('|');
  if (!undoAlt) return null;

  const sideUndo = n.match(
    new RegExp(`^(${chillicollectivoEscapeRegex(kw.customer)}|${chillicollectivoEscapeRegex(kw.fake)})\\s+(?:${pp}\\s+)?(${undoAlt})$`, 'i')
  );
  if (sideUndo) {
    return {
      side: chillicollectivoSideFromKeyword(sideUndo[1]),
    };
  }

  if (new RegExp(`^(?:${pp}\\s+)?(${undoAlt})$`, 'i').test(n)) {
    return { side: null };
  }

  return null;
}

function chillicollectivoApplyPPAutoFill(side, options = {}) {
  const appendExisting = options.appendExisting !== false;
  const info = chillicollectivoReadProfilInformationen(side);
  if (!info.root) {
    console.warn('[CHILLICOLLECTIVO] Profil-Panel nicht gefunden:', side);
    return { ok: false, filled: [], skipped: [], info };
  }

  const suggestions = chillicollectivoBuildPPAutoSuggestions(info, side);
  const filled = [];
  const skipped = [];
  let lastFilledEl = null;

  for (const item of suggestions) {
    if (CHILLICOLLECTIVO_PP_MANUAL_ONLY_FIELDS.has(item.fieldId)) {
      skipped.push({ ...item, reason: 'Sonstiges – nur manuell' });
      continue;
    }
    const fieldDef = CHILLICOLLECTIVO_PROFILE_FIELDS.find(f => f.id === item.fieldId);
    if (!fieldDef) continue;

    const el = chillicollectivoFindProfileField(side, fieldDef);
    if (!el) {
      skipped.push({ ...item, reason: 'Feld nicht gefunden' });
      continue;
    }

    const current = chillicollectivoGetProfileFieldValue(el);
    if (item.fieldId === 'name' && !chillicollectivoIsProfileFieldEmpty(current)) {
      skipped.push({ ...item, reason: 'Name bereits befuellt', current });
      continue;
    }
    const nextValue = appendExisting
      ? chillicollectivoAppendProfileValue(current, item.value)
      : item.value;
    if (nextValue === chillicollectivoCleanProfileValue(current)) {
      skipped.push({ ...item, reason: 'Bereits vorhanden', current });
      continue;
    }

    chillicollectivoWriteProfileField(el, nextValue, false, {
      source: 'pp-auto-fill',
      flow: 'pp-auto-fill',
      reason: item.source || 'auto-suggestion',
      extractionSource: item.source || 'auto-suggestion',
      dictationText: item.value,
      profileTextSnippet: chillicollectivoCombinedProfilText(info),
      append: appendExisting && !chillicollectivoIsProfileFieldEmpty(current),
    });
    lastFilledEl = el;
    filled.push({ ...item, previous: current, value: nextValue });
  }

  if (filled.length) {
    const customerCtx = chillicollectivoGetPPLogCustomerContext();
    const fm = globalThis.__chillicollectivoFieldManager;
    const route = typeof fm?._currentConversationKey === 'function' ? fm._currentConversationKey() : '';
    window.ChillicollectivoDiagnosticLog?.info?.('profile.auto-fill-done', `PP Auto-Fill ${side}: ${filled.length} Feld(er)`, {
      ...customerCtx,
      route,
      side,
      filledCount: filled.length,
      skippedCount: skipped.length,
      filledFields: filled.map((item) => ({
        fieldId: item.fieldId,
        value: String(item.value || '').slice(0, 200),
        previous: String(item.previous || '').slice(0, 200),
        source: item.source || '',
      })),
      hadCustomerMessage: customerCtx.hadCustomerMessage === true,
    }).catch?.(() => {});
  }

  console.log('[CHILLICOLLECTIVO] PP Auto-Fill', side, { filled, skipped, info });
  return { ok: true, filled, skipped, info, lastFilledEl };
}

function chillicollectivoNormalizePPAutoCommandText(text) {
  const pp = String(chillicollectivoGetProfileCmdConfig().ppKeyword || 'pp').toLowerCase();
  return chillicollectivoNormalizeProfileCommandText(text)
    .toLowerCase()
    .replace(/ß/g, 'ss')
    .replace(/\bp\s+p\b/g, pp)
    .replace(/profil\s*pflege/g, pp)
    .replace(/profilpflege/g, pp)
    .replace(/füllen/g, 'fuellen')
    .replace(/fullen/g, 'fuellen')
    .replace(/automatisch/g, 'auto')
    .replace(/auslesen/g, 'auto')
    .replace(/[.,!?;:]+$/g, '')
    .trim();
}

function chillicollectivoParseProfileAutoCommand(text) {
  const normalized = chillicollectivoNormalizePPAutoCommandText(text);
  if (!normalized) return null;

  const cfg = chillicollectivoGetProfileCmdConfig();
  const kw = chillicollectivoGetSideKeywords();
  const pp = chillicollectivoEscapeRegex(cfg.ppKeyword);
  const autoWords = chillicollectivoSplitAliasList(cfg.ppAutoAliases).map(chillicollectivoEscapeRegex).filter(Boolean).join('|');
  if (!autoWords) return null;

  const sideMatch = normalized.match(
    new RegExp(`^(${chillicollectivoEscapeRegex(kw.customer)}|${chillicollectivoEscapeRegex(kw.fake)})\\s+${pp}\\s+(${autoWords})$`, 'i')
  );
  if (sideMatch) {
    return {
      side: chillicollectivoSideFromKeyword(sideMatch[1]),
      action: 'pp_auto',
    };
  }

  const bothTriggers = chillicollectivoSplitAliasList(cfg.ppBothTriggers)
    .map((item) => chillicollectivoNormalizePPAutoCommandText(item))
    .filter(Boolean);
  if (bothTriggers.includes(normalized)) {
    return { side: CHILLICOLLECTIVO_SIDE_FAKE, action: 'pp_auto' };
  }

  return null;
}

function chillicollectivoProfileFieldMetaFromElement(el) {
  if (!el) return { side: null, fieldId: null };
  const side = chillicollectivoProfileSideOf(el);
  const label = chillicollectivoResolveFieldLabel(el);
  const fieldDef = CHILLICOLLECTIVO_PROFILE_FIELDS.find((f) => chillicollectivoLabelMatchesField(label, f));
  return { side, fieldId: fieldDef?.id || null };
}

function chillicollectivoClassifyDictationField(el) {
  if (!el || !chillicollectivoIsTorchwoodPage()) return null;
  const tag = String(el.tagName || '').toLowerCase();
  if (tag !== 'textarea' && !el.isContentEditable && tag !== 'input') return null;

  if (chillicollectivoIsNeueInfoTextarea(el)) {
    const side = chillicollectivoProfileSideOf(el) || 'center';
    return { area: `neue-info:${side}`, type: 'neue-info', side, fieldId: 'neue_info' };
  }
  if (chillicollectivoIsDialogInfoTextarea(el)) {
    const side = chillicollectivoProfileSideOf(el) || 'center';
    return { area: `dialog-info:${side}`, type: 'dialog-info', side, fieldId: 'dialog_info' };
  }
  if (chillicollectivoIsProfileTextarea(el)) {
    const meta = chillicollectivoProfileFieldMetaFromElement(el);
    const side = meta.side || '?';
    return { area: `pp:${side}`, type: 'pp', side, fieldId: meta.fieldId };
  }

  const vw = window.innerWidth || document.documentElement.clientWidth || 1;
  const vh = window.innerHeight || document.documentElement.clientHeight || 1;
  const rect = el.getBoundingClientRect();
  const cx = rect.left + rect.width / 2;
  if (rect.bottom > vh * 0.42 && cx > vw * 0.16 && cx < vw * 0.88) {
    return { area: 'compose', type: 'compose', side: null, fieldId: null };
  }
  if (tag === 'textarea' || el.isContentEditable) {
    const side = chillicollectivoProfileSideOf(el);
    if (side) return { area: `pp:${side}`, type: 'pp', side, fieldId: null };
    return { area: 'compose', type: 'compose', side: null, fieldId: null };
  }
  if (tag === 'input' && chillicollectivoIsDictationTextControl(el)) {
    const side = chillicollectivoProfileSideOf(el);
    if (side) return { area: `pp:${side}`, type: 'pp', side, fieldId: null };
  }
  return null;
}

function chillicollectivoResolveProfileLockField(lock) {
  if (!lock) return null;
  if (lock.field && document.contains(lock.field) && chillicollectivoIsProfileTextarea(lock.field)) {
    return lock.field;
  }
  if (lock.side && lock.fieldId) {
    const fieldDef = CHILLICOLLECTIVO_PROFILE_FIELDS.find((f) => f.id === lock.fieldId);
    const fresh = fieldDef ? chillicollectivoFindProfileField(lock.side, fieldDef) : null;
    if (fresh) {
      lock.field = fresh;
      return fresh;
    }
  }
  return lock.field && document.contains(lock.field) ? lock.field : null;
}

function chillicollectivoSyncProfileDictationTarget(fieldManager) {
  const lock = fieldManager?._profileValueLock;
  if (!lock || lock.until <= Date.now()) return null;
  const fresh = chillicollectivoResolveProfileLockField(lock);
  if (!fresh) return null;
  lock.field = fresh;
  if (fieldManager.currentState === 'recording') {
    fieldManager.recordingField = fresh;
    fieldManager.activeField = fresh;
    const hasSelection = typeof fresh.selectionStart === 'number'
      && typeof fresh.selectionEnd === 'number'
      && fresh.selectionStart !== fresh.selectionEnd;
    if (hasSelection) {
      fieldManager.recordingStartPosition = fresh.selectionStart;
      fieldManager.recordingSelectionEnd = fresh.selectionEnd;
    } else {
      fieldManager.recordingStartPosition = fresh.selectionStart ?? (fresh.value || '').length;
      fieldManager.recordingSelectionEnd = fieldManager.recordingStartPosition;
    }
  }
  return fresh;
}

function chillicollectivoProfileFieldHasSelection(field) {
  if (!field || typeof field.selectionStart !== 'number' || typeof field.selectionEnd !== 'number') return false;
  return field.selectionStart !== field.selectionEnd;
}

function chillicollectivoLogProfileFocus(event, message, detail = {}) {
  window.ChillicollectivoDiagnosticLog?.info?.('profile.focus', message, {
    event,
    ...detail,
  }).catch?.(() => {});
}

function chillicollectivoReclaimProfileUiFocus(fieldManager) {
  const lock = fieldManager?._profileValueLock;
  if (!lock || lock.until <= Date.now()) return null;
  const fresh = chillicollectivoResolveProfileLockField(lock);
  if (!fresh) return null;
  lock.field = fresh;

  if (fieldManager._profileUserSelecting) {
    chillicollectivoLogProfileFocus('reclaim-skipped', 'PP-Fokus-Reclaim übersprungen – Nutzer markiert gerade', {
      fieldId: lock.fieldId,
      side: lock.side,
      reason: 'user-selecting',
    });
    return fresh;
  }

  const active = document.activeElement;
  if (active === fresh) {
    if (chillicollectivoProfileFieldHasSelection(fresh)) {
      chillicollectivoLogProfileFocus('reclaim-skipped', 'PP-Fokus-Reclaim übersprungen – Markierung aktiv', {
        fieldId: lock.fieldId,
        side: lock.side,
        selectionStart: fresh.selectionStart,
        selectionEnd: fresh.selectionEnd,
        reason: 'active-selection',
      });
      return fresh;
    }
    chillicollectivoLogProfileFocus('reclaim-skipped', 'PP-Fokus-Reclaim übersprungen – Feld hat bereits Fokus', {
      fieldId: lock.fieldId,
      side: lock.side,
      reason: 'already-focused',
    });
    return fresh;
  }

  try {
    fresh.focus?.({ preventScroll: true });
    if (!chillicollectivoProfileFieldHasSelection(fresh)) {
      const pos = fresh.selectionStart ?? (fresh.value || '').length;
      fresh.setSelectionRange?.(pos, pos);
    }
  } catch (_) {}
  chillicollectivoLogProfileFocus('reclaim', 'PP-Fokus zurückgeholt', {
    fieldId: lock.fieldId,
    side: lock.side,
    hadSelection: chillicollectivoProfileFieldHasSelection(fresh),
    selectionStart: fresh.selectionStart,
    selectionEnd: fresh.selectionEnd,
  });
  if (fieldManager.currentState === 'recording') {
    fieldManager.recordingField = fresh;
    fieldManager.activeField = fresh;
    if (chillicollectivoProfileFieldHasSelection(fresh)) {
      fieldManager.recordingStartPosition = fresh.selectionStart;
      fieldManager.recordingSelectionEnd = fresh.selectionEnd;
    } else {
      fieldManager.recordingStartPosition = fresh.selectionStart ?? (fresh.value || '').length;
      fieldManager.recordingSelectionEnd = fieldManager.recordingStartPosition;
    }
  }
  return fresh;
}

function chillicollectivoCancelProfileFocusReclaim(fieldManager) {
  if (!fieldManager?._profileFocusReclaimTimers?.length) return;
  fieldManager._profileFocusReclaimTimers.forEach((id) => clearTimeout(id));
  fieldManager._profileFocusReclaimTimers = [];
}

function chillicollectivoScheduleProfileFocusReclaim(fieldManager) {
  chillicollectivoCancelProfileFocusReclaim(fieldManager);
  const reclaim = () => {
    if (!(fieldManager?._profileValueLock?.until > Date.now())) return;
    if ((fieldManager?._composeFocusGuardUntil || 0) > Date.now()) return;
    if (fieldManager._profileUserSelecting) return;
    const lockField = chillicollectivoResolveProfileLockField(fieldManager._profileValueLock);
    if (lockField && document.activeElement === lockField) return;
    chillicollectivoReclaimProfileUiFocus(fieldManager);
  };
  fieldManager._profileFocusReclaimTimers = [0, 120, 320, 700, 1400]
    .map((ms) => setTimeout(reclaim, ms));
}

function chillicollectivoClearProfileDictationLock(fieldManager) {
  if (!fieldManager) return;
  chillicollectivoCancelProfileFocusReclaim(fieldManager);
  fieldManager._profileValueLock = null;
  if (fieldManager._profileFieldObserver) {
    fieldManager._profileFieldObserver.disconnect();
    fieldManager._profileFieldObserver = null;
  }
  fieldManager._profileFocusGuardActive = false;
}

function chillicollectivoReturnToComposeAfterProfileValue(fieldManager) {
  if (!fieldManager) return false;
  if (fieldManager.currentState === 'recording') {
    return false;
  }
  chillicollectivoClearProfileDictationLock(fieldManager);
  fieldManager._composeFocusGuardUntil = Date.now() + 12000;

  const adopt = () => {
    if ((fieldManager._composeFocusGuardUntil || 0) <= Date.now()) return false;
    if (typeof fieldManager._adoptFreshComposeField === 'function' && fieldManager._adoptFreshComposeField()) {
      const compose = fieldManager.recordingField || fieldManager.activeField;
      if (compose) {
        try {
          compose.focus?.({ preventScroll: true });
          if (compose.isContentEditable) {
            const range = document.createRange();
            range.selectNodeContents(compose);
            range.collapse(false);
            const sel = window.getSelection();
            sel?.removeAllRanges();
            sel?.addRange(range);
          } else {
            const len = String(compose.value ?? '').length;
            compose.setSelectionRange?.(len, len);
          }
        } catch (_) {}
        fieldManager._userTargetField = compose;
        if (fieldManager.currentState === 'recording') {
          fieldManager._dictationTargetKey = typeof fieldManager._getFieldTargetKey === 'function'
            ? fieldManager._getFieldTargetKey(compose)
            : '';
        }
      }
      return !!compose;
    }
    const compose = typeof fieldManager._findComposeTextField === 'function'
      ? fieldManager._findComposeTextField()
      : null;
    if (!compose) return false;
    try {
      compose.focus?.({ preventScroll: true });
      if (compose.isContentEditable) {
        const range = document.createRange();
        range.selectNodeContents(compose);
        range.collapse(false);
        const sel = window.getSelection();
        sel?.removeAllRanges();
        sel?.addRange(range);
      } else {
        const len = String(compose.value ?? '').length;
        compose.setSelectionRange?.(len, len);
      }
    } catch (_) {}
    fieldManager.activeField = compose;
    fieldManager._userTargetField = compose;
    if (fieldManager.currentState === 'recording') {
      fieldManager.recordingField = compose;
      fieldManager.recordingStartPosition = compose.isContentEditable
        ? null
        : (compose.value || '').length;
      fieldManager._dictationTargetKey = typeof fieldManager._getFieldTargetKey === 'function'
        ? fieldManager._getFieldTargetKey(compose)
        : '';
    }
    return true;
  };

  const scheduled = [0, 16, 40, 90, 180, 360, 700, 1200, 2000];
  let ok = adopt();
  queueMicrotask(adopt);
  if (typeof requestAnimationFrame === 'function') requestAnimationFrame(adopt);
  scheduled.forEach((ms) => setTimeout(adopt, ms));
  return ok;
}

function chillicollectivoEnsureProfileFieldWatcher(fieldManager) {
  if (fieldManager._profileFieldObserver) return;
  fieldManager._profileFieldObserver = new MutationObserver(() => {
    const lock = fieldManager._profileValueLock;
    if (!lock || lock.until <= Date.now()) return;
    const fresh = chillicollectivoResolveProfileLockField(lock);
    if (!fresh) return;
    lock.field = fresh;
    if (fieldManager.currentState === 'recording') {
      fieldManager.recordingField = fresh;
      fieldManager.activeField = fresh;
      if (chillicollectivoProfileFieldHasSelection(fresh)) {
        fieldManager.recordingStartPosition = fresh.selectionStart;
        fieldManager.recordingSelectionEnd = fresh.selectionEnd;
      } else if (!fieldManager._profileUserSelecting) {
        fieldManager.recordingStartPosition = fresh.selectionStart ?? (fresh.value || '').length;
        fieldManager.recordingSelectionEnd = fieldManager.recordingStartPosition;
      }
    }
  });
}

function chillicollectivoStartProfileFieldWatcher(fieldManager, side) {
  if (!side) return;
  chillicollectivoEnsureProfileFieldWatcher(fieldManager);
  const root = chillicollectivoFindSideProfileRoot(side);
  if (!root) return;
  fieldManager._profileFieldObserver.disconnect();
  fieldManager._profileFieldObserver.observe(root, { childList: true, subtree: true, attributes: true });
  fieldManager._profileFieldWatchSide = side;
}

function chillicollectivoStickProfileFieldFocus(fieldManager, el, lockMs = 20000, lockMeta = null, options = {}) {
  if (!el || !fieldManager) return;
  const preserveSelection = options.preserveSelection === true;
  const derived = {
    side: lockMeta?.side ?? chillicollectivoProfileFieldMetaFromElement(el).side,
    fieldId: lockMeta?.fieldId ?? chillicollectivoProfileFieldMetaFromElement(el).fieldId,
  };
  let target = el;
  if (derived.side && derived.fieldId) {
    const resolved = chillicollectivoResolveProfileLockField({ field: el, side: derived.side, fieldId: derived.fieldId });
    if (resolved) target = resolved;
  }

  fieldManager._profileValueLock = {
    field: target,
    side: derived.side,
    fieldId: derived.fieldId,
    until: Date.now() + lockMs,
  };

  if (fieldManager.currentState === 'recording') {
    fieldManager.recordingField = target;
    fieldManager.activeField = target;
    if (chillicollectivoProfileFieldHasSelection(target)) {
      fieldManager.recordingStartPosition = target.selectionStart;
      fieldManager.recordingSelectionEnd = target.selectionEnd;
    } else {
      fieldManager.recordingStartPosition = target.selectionStart ?? (target.value || '').length;
      fieldManager.recordingSelectionEnd = fieldManager.recordingStartPosition;
    }
  }

  const hadSelection = chillicollectivoProfileFieldHasSelection(target);
  try {
    if (preserveSelection) {
      if (document.activeElement !== target) target.focus?.({ preventScroll: true });
    } else {
      target.focus?.({ preventScroll: true });
      if (!hadSelection) {
        const pos = target.selectionStart ?? (target.value || '').length;
        target.setSelectionRange?.(pos, pos);
      }
    }
  } catch (_) {}

  chillicollectivoLogProfileFocus('stick', preserveSelection ? 'PP-Feld fokussiert (Markierung erhalten)' : 'PP-Feld fokussiert', {
    fieldId: derived.fieldId,
    side: derived.side,
    preserveSelection,
    hadSelection,
    selectionStart: target.selectionStart,
    selectionEnd: target.selectionEnd,
    trigger: options.trigger || 'unknown',
  });

  if (derived.side) chillicollectivoStartProfileFieldWatcher(fieldManager, derived.side);
  if (!preserveSelection) chillicollectivoScheduleProfileFocusReclaim(fieldManager);
}

function chillicollectivoShouldPPDirectInsert(fieldManager) {
  if (!fieldManager) return false;
  if (typeof fieldManager._shouldPPDirectInsert === 'function') {
    return fieldManager._shouldPPDirectInsert();
  }
  if (typeof fieldManager._isPPAutoInsertEnabled === 'function') {
    return fieldManager._isPPAutoInsertEnabled();
  }
  return false;
}

function chillicollectivoIsProfileDictationContext(fieldManager) {
  if (!fieldManager) return false;
  if (fieldManager._profileValueLock?.until > Date.now()) return true;

  const fields = [
    fieldManager._userTargetField,
    fieldManager.recordingField,
    fieldManager.activeField,
    document.activeElement,
  ];
  for (const el of fields) {
    if (!el) continue;
    const classified = chillicollectivoClassifyDictationField(el);
    if (classified && ['pp', 'neue-info', 'dialog-info'].includes(classified.type)) return true;
  }
  return false;
}

function chillicollectivoTryHandleProfileCommand(fieldManager, text) {
  if (!chillicollectivoIsTorchwoodPage()) return false;
  if (!chillicollectivoIsProfileDictationContext(fieldManager)) return false;

  if (!chillicollectivoLooksLikeProfileCommand(text)) return false;

  const parsed = chillicollectivoParseProfileCommand(text);
  if (!parsed) return false;

  const fieldDef = CHILLICOLLECTIVO_PROFILE_FIELDS.find(f => f.id === parsed.fieldId);
  if (!fieldDef) return false;

  const oldLock = fieldManager?._profileValueLock;
  if (oldLock && (oldLock.side !== parsed.side || oldLock.fieldId !== parsed.fieldId)) {
    chillicollectivoClearProfileDictationLock(fieldManager);
    window.ChillicollectivoDiagnosticLog?.info?.('profile.command', 'Profilpflege-Lock fuer neuen expliziten Befehl gewechselt', {
      from: { side: oldLock.side, fieldId: oldLock.fieldId },
      to: { side: parsed.side, fieldId: parsed.fieldId },
      text,
    }).catch?.(() => {});
  }

  const el = chillicollectivoFindProfileField(parsed.side, fieldDef);
  if (!el) {
    console.warn('[CHILLICOLLECTIVO] Profilfeld nicht gefunden:', parsed.side, parsed.fieldLabel);
    return true;
  }

  const lockMeta = { side: parsed.side, fieldId: parsed.fieldId };
  let appendMode = false;
  let returnedToCompose = false;
  let duplicateValue = false;

  if (parsed.value) {
    const currentValue = chillicollectivoGetProfileFieldValue(el);
    if (parsed.fieldId === 'name' && !chillicollectivoIsProfileFieldEmpty(currentValue)) {
      fieldManager.fab?.showPreview?.('Name schon vorhanden', 'Nicht ersetzt');
      setTimeout(() => fieldManager.fab?.hidePreview?.(), 1800);
      chillicollectivoReturnToComposeAfterProfileValue(fieldManager);
      return true;
    }

    if (!chillicollectivoShouldPPDirectInsert(fieldManager)) {
      chillicollectivoStickProfileFieldFocus(fieldManager, el, 20000, lockMeta);
      fieldManager._currentInterimText = '';
      fieldManager._lastInterim = '';
      fieldManager._stopFabMotion?.();
      if (typeof fieldManager._bufferFinalToPreviewOnly === 'function') {
        fieldManager._bufferFinalToPreviewOnly(parsed.value);
      } else if (fieldManager.fab?.showPreview) {
        fieldManager.fab.showPreview(parsed.value, '', {
          insertHint: typeof fieldManager._getPreviewInsertHint === 'function'
            ? fieldManager._getPreviewInsertHint()
            : '',
        });
      }
      return true;
    }

    const nextValue = chillicollectivoAppendProfileValue(currentValue, parsed.value);
    duplicateValue = nextValue === chillicollectivoCleanProfileValue(currentValue);
    chillicollectivoPushPPUndoEntry([{
      side: parsed.side,
      fields: [{
        fieldId: parsed.fieldId,
        value: currentValue,
        el,
      }],
    }]);
    appendMode = !chillicollectivoIsProfileFieldEmpty(currentValue);
    if (!duplicateValue) {
      chillicollectivoWriteProfileField(el, nextValue, false, {
        source: 'voice-command',
        flow: 'voice-command',
        extractionSource: parsed.inferred ? 'inferred-command' : 'explicit-command',
        reason: parsed.inferred ? 'inferred-command' : 'explicit-command',
        dictationText: parsed.value,
        append: appendMode,
      });
    } else {
      const customerCtx = chillicollectivoGetPPLogCustomerContext();
      window.ChillicollectivoDiagnosticLog?.info?.('profile.insert-skipped', `PP ${parsed.side}/${parsed.fieldLabel}: bereits vorhanden`, {
        ...customerCtx,
        side: parsed.side,
        fieldId: parsed.fieldId,
        fieldLabel: parsed.fieldLabel,
        dictationText: String(parsed.value || '').slice(0, 200),
        currentValue: String(currentValue || '').slice(0, 200),
        source: 'voice-command',
        reason: 'duplicate-value',
        hadCustomerMessage: customerCtx.hadCustomerMessage === true,
      }).catch?.(() => {});
    }
    returnedToCompose = chillicollectivoReturnToComposeAfterProfileValue(fieldManager);
    window.ChillicollectivoDiagnosticLog?.info?.('profile.command', 'Profilpflege-Wert eingetragen, Ruecksprung ins Chatfeld versucht', {
      side: parsed.side,
      fieldId: parsed.fieldId,
      fieldLabel: parsed.fieldLabel,
      inferred: !!parsed.inferred,
      duplicateValue,
      returnedToCompose,
      dictationText: String(parsed.value || '').slice(0, 200),
      valueAfter: duplicateValue ? String(currentValue || '').slice(0, 200) : String(nextValue || '').slice(0, 200),
    }).catch?.(() => {});
  } else {
    chillicollectivoStickProfileFieldFocus(fieldManager, el, 20000, lockMeta);
  }

  if (!parsed.value) {
    const fresh = chillicollectivoResolveProfileLockField(fieldManager._profileValueLock);
    if (fresh) chillicollectivoStickProfileFieldFocus(fieldManager, fresh, 20000, lockMeta);
  }

  fieldManager.fab?.showPreview?.(
    parsed.value
      ? `${parsed.fieldLabel}: ${duplicateValue ? 'schon vorhanden' : (appendMode ? 'ergänzt' : 'gesetzt')}`
      : `${parsed.fieldLabel}: Fokus`,
    parsed.fieldId === 'sonstiges' && parsed.value
      ? 'Weiteres wird angehängt'
      : (parsed.value ? 'Zurück im Chatfeld' : 'Weiter im PP-Feld diktieren')
  );
  setTimeout(() => fieldManager.fab?.hidePreview?.(), 2200);

  return true;
}

function chillicollectivoNormalizeGoSendText(text) {
  return String(text || '')
    .trim()
    .toLowerCase()
    .replace(/[.,!?;:]+$/g, '')
    .trim();
}

function chillicollectivoIsSmileyTriggerFragment(text) {
  return typeof chillicollectivoIsVoiceSmileyTrigger === 'function'
    && chillicollectivoIsVoiceSmileyTrigger(text);
}

function chillicollectivoGetGoHiddenStripList() {
  const cfg = chillicollectivoGetProfileCmdConfig();
  return chillicollectivoSplitAliasList(cfg.goHiddenStrip)
    .map((item) => item.toLowerCase())
    .filter((item) => item && !chillicollectivoIsSmileyTriggerFragment(item));
}

function chillicollectivoBuildGoHiddenStripTrailingPattern() {
  const parts = chillicollectivoGetGoHiddenStripList().map((phrase) =>
    chillicollectivoEscapeRegex(phrase).replace(/\s+/g, '\\s+'),
  );
  return parts.length ? new RegExp(`(\\s+(?:${parts.join('|')}))\\s*[.,!?;:]*\\s*$`, 'i') : null;
}

function chillicollectivoIsHiddenGoStripFragment(text) {
  const norm = chillicollectivoNormalizeGoSendText(text);
  if (!norm || chillicollectivoIsSmileyTriggerFragment(norm)) return false;
  return chillicollectivoGetGoHiddenStripList().includes(norm);
}

function chillicollectivoStripHiddenGoPhrases(text) {
  let t = String(text || '');
  if (!t.trim()) return '';
  if (chillicollectivoIsHiddenGoStripFragment(t)) return '';

  const trailingRe = chillicollectivoBuildGoHiddenStripTrailingPattern();
  let guard = 0;
  while (guard++ < 8 && trailingRe) {
    const trailing = t.match(trailingRe);
    const frag = chillicollectivoNormalizeGoSendText(String(trailing?.[1] || '').trim());
    if (trailing && chillicollectivoIsHiddenGoStripFragment(frag)) {
      t = t.slice(0, t.length - trailing[0].length);
      continue;
    }
    break;
  }

  guard = 0;
  while (guard++ < 8) {
    const leading = t.match(/^\s*((?:\S+\s*)*?\S+)\s*[.,!?;:]*(?=\s|$)/);
    const frag = chillicollectivoNormalizeGoSendText(leading?.[1] || '');
    if (leading && chillicollectivoIsHiddenGoStripFragment(frag)) {
      t = t.slice(leading[0].length);
      continue;
    }
    break;
  }

  const parts = chillicollectivoGetGoHiddenStripList().map((phrase) =>
    chillicollectivoEscapeRegex(phrase).replace(/\s+/g, '\\s+'),
  );
  if (parts.length) {
    t = t.replace(new RegExp(`\\s+(?:${parts.join('|')})(?=\\s|$|[.,!?;:])`, 'gi'), '');
  }

  return t.replace(/\s{2,}/g, ' ').trim();
}

function chillicollectivoGetGoSendPhraseList() {
  const cfg = chillicollectivoGetProfileCmdConfig();
  return chillicollectivoSplitAliasList(cfg.goExact)
    .map((item) => item.toLowerCase())
    .filter((item) => item && !chillicollectivoIsSmileyTriggerFragment(item));
}

function chillicollectivoBuildGoSendExactPattern() {
  const parts = chillicollectivoGetGoSendPhraseList().map((phrase) =>
    chillicollectivoEscapeRegex(phrase).replace(/\s+/g, '\\s+'),
  );
  return parts.length ? new RegExp(`^(?:${parts.join('|')})$`, 'i') : null;
}

function chillicollectivoBuildGoSendTrailingPattern() {
  const parts = chillicollectivoGetGoSendPhraseList().map((phrase) =>
    chillicollectivoEscapeRegex(phrase).replace(/\s+/g, '\\s+'),
  );
  return parts.length ? new RegExp(`(\\s+(?:${parts.join('|')}))\\s*[.,!?;:]*\\s*$`, 'i') : null;
}

function chillicollectivoIsGoSendFragment(text) {
  const norm = chillicollectivoNormalizeGoSendText(text);
  if (!norm || chillicollectivoIsSmileyTriggerFragment(norm)) return false;
  const exactList = chillicollectivoGetGoSendPhraseList();
  if (exactList.includes(norm)) return true;
  const exactRe = chillicollectivoBuildGoSendExactPattern();
  return !!(exactRe && exactRe.test(norm));
}

function chillicollectivoParseGoSendCommand(text) {
  return chillicollectivoIsGoSendFragment(text);
}

function chillicollectivoNormalizeMissedSendwordText(text) {
  return String(text || '')
    .toLowerCase()
    .replaceAll('ä', 'ae')
    .replaceAll('ö', 'oe')
    .replaceAll('ü', 'ue')
    .replaceAll('ß', 'ss')
    .replace(/[^a-z0-9\s-]+/g, ' ')
    .replace(/\s+/g, ' ')
    .trim();
}

function chillicollectivoLooksLikeMissedSendword(text) {
  const norm = chillicollectivoNormalizeMissedSendwordText(text);
  if (!norm || chillicollectivoIsSmileyTriggerFragment(norm)) return false;
  const parts = chillicollectivoGetGoSendPhraseList().map((phrase) =>
    chillicollectivoEscapeRegex(chillicollectivoNormalizeMissedSendwordText(phrase)).replace(/\s+/g, '\\s+'),
  );
  if (!parts.length) return false;
  return new RegExp(`\\b(?:${parts.join('|')})\\b`, 'i').test(norm);
}

function chillicollectivoStripGoSendTokens(text) {
  let t = chillicollectivoStripHiddenGoPhrases(text);
  if (!t.trim()) return '';

  const trailingRe = chillicollectivoBuildGoSendTrailingPattern();
  let guard = 0;
  while (guard++ < 8 && trailingRe) {
    const trailing = t.match(trailingRe);
    const fragment = String(trailing?.[1] || trailing?.[0] || '').trim();
    if (trailing && chillicollectivoIsGoSendFragment(fragment)) {
      t = t.slice(0, t.length - trailing[0].length);
      continue;
    }
    break;
  }

  guard = 0;
  while (guard++ < 8) {
    const leading = t.match(/^\s*(\S(?:.*?\S)?)\s*[.,!?;:]*(?=\s|$)/);
    if (leading && chillicollectivoIsGoSendFragment(leading[1])) {
      t = t.slice(leading[0].length);
      continue;
    }
    break;
  }

  return t.replace(/\s{2,}/g, ' ').trim();
}

function chillicollectivoSplitGoSendFromText(text) {
  const raw = String(text || '');
  if (!raw.trim()) return { text: '', triggerSend: false };

  let cleaned = chillicollectivoStripHiddenGoPhrases(raw);
  if (!cleaned.trim()) return { text: '', triggerSend: false };
  if (chillicollectivoParseGoSendCommand(cleaned)) return { text: '', triggerSend: true };

  let triggerSend = false;
  const trailingRe = chillicollectivoBuildGoSendTrailingPattern();
  let guard = 0;
  while (guard++ < 8 && trailingRe) {
    const match = cleaned.match(trailingRe);
    const fragment = String(match?.[1] || match?.[0] || '').trim();
    if (!match || !chillicollectivoIsGoSendFragment(fragment)) break;
    cleaned = cleaned.slice(0, cleaned.length - match[0].length).trimEnd();
    triggerSend = true;
  }

  cleaned = chillicollectivoStripGoSendTokens(cleaned);
  return { text: cleaned, triggerSend };
}

function chillicollectivoFormatSendwordBadge(phrase) {
  const norm = chillicollectivoNormalizeGoSendText(phrase);
  if (!norm) return 'KUSSI';
  if (/^kuss|^buss|^kus/.test(norm) || norm === 'kuss') return 'KUSSI';
  if (norm === 'zack' || norm.startsWith('zack')) return 'ZACK';
  if (norm.includes('absend')) return 'ABSENDEN';
  if (norm.includes('wegschick')) return 'WEGSCHICKEN';
  if (norm.includes('wegsend')) return 'WEGSENDEN';
  return norm.toUpperCase();
}

function chillicollectivoFindTrailingSendwordPartial(text) {
  const raw = String(text || '').trim();
  const match = raw.match(/^(.*?)(\s+)(\S+)\s*$/);
  if (!match) return null;

  const head = String(match[1] || '').trim();
  const tail = chillicollectivoNormalizeGoSendText(match[3]);
  if (!tail || chillicollectivoIsSmileyTriggerFragment(tail)) return null;

  const phrases = chillicollectivoGetGoSendPhraseList();
  let best = null;
  for (const phrase of phrases) {
    const pNorm = chillicollectivoNormalizeGoSendText(phrase);
    if (!pNorm || chillicollectivoIsSmileyTriggerFragment(pNorm)) continue;
    if (tail === pNorm || pNorm.startsWith(tail)) {
      if (!best || pNorm.length < best.phrase.length) {
        best = {
          head,
          phrase: pNorm,
          fragment: tail,
          isComplete: tail === pNorm,
        };
      }
    }
  }
  if (!best) return null;
  if (chillicollectivoIsSmileyTriggerFragment(best.fragment || best.phrase)) return null;
  if (!best.isComplete) {
    const kussLike = /^(?:ku|bu)/i.test(best.fragment);
    const minLen = kussLike ? 2 : 3;
    if (best.fragment.length < minLen && !/^[kz]$/.test(best.fragment)) return null;
  }
  return best;
}

function chillicollectivoSplitPreviewSendword(text) {
  const raw = String(text || '');
  const strippedHidden = chillicollectivoStripHiddenGoPhrases(raw);
  if (chillicollectivoParseGoSendCommand(strippedHidden)) {
    return {
      displayText: '',
      sendwordBadge: chillicollectivoFormatSendwordBadge(strippedHidden),
      triggerSend: true,
    };
  }

  const full = chillicollectivoSplitGoSendFromText(raw);
  const partial = chillicollectivoFindTrailingSendwordPartial(raw);

  if (full.triggerSend) {
    return {
      displayText: full.text,
      sendwordBadge: chillicollectivoFormatSendwordBadge(partial?.phrase || strippedHidden || 'kussi'),
      triggerSend: true,
    };
  }

  if (partial) {
    return {
      displayText: partial.head,
      sendwordBadge: chillicollectivoFormatSendwordBadge(partial.phrase),
      triggerSend: partial.isComplete,
    };
  }

  return {
    displayText: full.text || chillicollectivoStripGoSendTokens(raw),
    sendwordBadge: '',
    triggerSend: false,
  };
}

function chillicollectivoStripGoFromEditableField() {
  return false;
}

chillicollectivoInitProfileCommandConfig();
globalThis.chillicollectivoIsProfileTextarea = chillicollectivoIsProfileTextarea;
globalThis.chillicollectivoIsNeueInfoTextarea = chillicollectivoIsNeueInfoTextarea;
globalThis.chillicollectivoIsDialogInfoTextarea = chillicollectivoIsDialogInfoTextarea;
globalThis.chillicollectivoClassifyDictationField = chillicollectivoClassifyDictationField;
globalThis.chillicollectivoStickProfileFieldFocus = chillicollectivoStickProfileFieldFocus;
globalThis.chillicollectivoSyncProfileDictationTarget = chillicollectivoSyncProfileDictationTarget;
globalThis.chillicollectivoClearProfileDictationLock = chillicollectivoClearProfileDictationLock;
globalThis.chillicollectivoResolveProfileLockField = chillicollectivoResolveProfileLockField;
globalThis.chillicollectivoReclaimProfileUiFocus = chillicollectivoReclaimProfileUiFocus;
globalThis.chillicollectivoScheduleProfileFocusReclaim = chillicollectivoScheduleProfileFocusReclaim;
globalThis.chillicollectivoReturnToComposeAfterProfileValue = chillicollectivoReturnToComposeAfterProfileValue;
globalThis.chillicollectivoWriteProfileField = chillicollectivoWriteProfileField;
globalThis.chillicollectivoStripEmojisFromProfileText = chillicollectivoStripEmojisFromProfileText;
globalThis.chillicollectivoProfileFieldMetaFromElement = chillicollectivoProfileFieldMetaFromElement;
globalThis.chillicollectivoIsProfileDictationContext = chillicollectivoIsProfileDictationContext;
globalThis.chillicollectivoLooksLikeProfileCommand = chillicollectivoLooksLikeProfileCommand;
globalThis.chillicollectivoParseGoSendCommand = chillicollectivoParseGoSendCommand;
globalThis.chillicollectivoSplitGoSendFromText = chillicollectivoSplitGoSendFromText;
globalThis.chillicollectivoTryHandleProfileCommand = chillicollectivoTryHandleProfileCommand;
globalThis.chillicollectivoStripGoSendTokens = chillicollectivoStripGoSendTokens;
globalThis.chillicollectivoStripGoFromEditableField = chillicollectivoStripGoFromEditableField;
globalThis.chillicollectivoCollectVorliebenKeywords = chillicollectivoCollectVorliebenKeywords;
globalThis.chillicollectivoExtractCustomerRelationshipFacts = chillicollectivoExtractCustomerRelationshipFacts;
globalThis.chillicollectivoLooksLikeMissedSendword = chillicollectivoLooksLikeMissedSendword;
globalThis.chillicollectivoGetGoSendPhraseList = chillicollectivoGetGoSendPhraseList;
globalThis.chillicollectivoSplitPreviewSendword = chillicollectivoSplitPreviewSendword;
globalThis.chillicollectivoFormatSendwordBadge = chillicollectivoFormatSendwordBadge;
