/* YeYe Expat Centre — Pages (all translated) */
const P = window.YEYE;
const GHL_READ_BASE = 'https://ghl-read-service-ib6dfdthbq-ew.a.run.app';
const GHL_WRITE_BASE = (window.YEYE_CONFIG && (window.YEYE_CONFIG.WRITE_API_URL || window.YEYE_CONFIG.GHL_WRITE_API_URL)) || 'https://ghl-write-service-ib6dfdthbq-ew.a.run.app';
const GHL_WRITE_KEY = (window.YEYE_CONFIG && (window.YEYE_CONFIG.WRITE_API_KEY || window.YEYE_CONFIG.GHL_WRITE_API_KEY)) || '';

function PageHead({ title, sub, actions }) {
  return (
    <div className="phead">
      <div className="pt"><h1 className="h1">{title}</h1>{sub && <div className="muted">{sub}</div>}</div>
      {actions && <div className="flex gap-8 wrap">{actions}</div>}
    </div>
  );
}
function svcName(s, t, lang = 'en') {
  const nameKey = 'services.name.' + s.k;
  const translatedName = t(nameKey);
  if (translatedName !== nameKey) return translatedName;
  const legacyKey = 'svc.' + s.k;
  const translatedLegacy = t(legacyKey);
  return translatedLegacy === legacyKey ? (localizedText(s.name, lang) || s.k) : translatedLegacy;
}
function localizedText(value, lang = 'en') {
  if (!value || typeof value !== 'object') return value;
  return value[lang] || value.en || value.tr || value.cs || '';
}
function svcCatName(s, t) {
  const translated = t('scat.' + s.cat);
  return translated === 'scat.' + s.cat ? (s.catName || (P.catLabels && P.catLabels[s.cat]) || s.cat) : translated;
}
const CURRENCY_RATES = { EUR: 1, USD: 1.2, CZK: 24.24, TRY: 53.72 };
function eurValue(s) {
  const m = String((s && s.p) || '').match(/€\s*([0-9]+(?:[.,][0-9]+)?)/);
  return m ? Number(m[1].replace(',', '.')) : null;
}
function moneyLabel(value, currency) {
  if (!Number.isFinite(value)) return '';
  const digits = currency === 'CZK' ? 0 : 2;
  const amount = Number(value).toLocaleString('tr-TR', { minimumFractionDigits: digits, maximumFractionDigits: digits });
  if (currency === 'CZK') return amount + ' CZK';
  if (currency === 'TRY') return amount + ' TL';
  if (currency === 'USD') return '$' + amount;
  return '€' + amount;
}
function marketplaceAmount(serviceKey, currency) {
  if (!serviceKey) return null;
  const products = (P && P.marketplaceProducts) || [];
  const product = products.find((item) => item.key === serviceKey || (item.serviceKeys || []).includes(serviceKey));
  const variant = product && (product.variants || []).find((item) => item.serviceKey === serviceKey);
  const amounts = (variant && variant.amounts) || (product && product.amounts);
  if (!amounts || !Object.prototype.hasOwnProperty.call(amounts, currency)) return null;
  return Number(amounts[currency]);
}
function svcPrice(s, currency = 'EUR', t) {
  const eur = eurValue(s);
  const ghlAmount = marketplaceAmount(s && s.k, currency);
  const rawPrice = s.priceLabel || s.p || 'Ask for price';
  if (/€\s*[0-9]+(?:[.,][0-9]+)?\s*[–-]\s*[0-9]/.test(rawPrice)) return rawPrice;
  if (ghlAmount == null && eur == null) return rawPrice === 'Ask for price' && t ? t('svc.askForPrice') : rawPrice;
  const unitKeys = {
    '/ Item': 'svc.unit.item',
    '/ One Time': 'svc.unit.oneTime',
    '/ Month': 'svc.unit.month',
    '/ Annum': 'svc.unit.year',
    '/ Hour': 'svc.unit.hour',
  };
  const rawUnit = String(s.u || '').trim();
  const unit = rawUnit && t && unitKeys[rawUnit] ? ' ' + t(unitKeys[rawUnit]) : (s.u || '');
  const amount = ghlAmount == null ? eur * (CURRENCY_RATES[currency] || 1) : ghlAmount;
  return moneyLabel(amount, currency) + unit;
}

/* ===== SERVICES — real catalog grouped by category ===== */
function isFreshUserEmail() {
  try {
    const u = window.YEYE_AUTH && window.YEYE_AUTH.getState && window.YEYE_AUTH.getState().user;
    const e = u && u.email;
    return !!e && ['toptenvideoyou@gmail.com', 'testblue.demo+20260619@yeye-internal.example.com'].includes(e.toLowerCase());
  } catch (_) { return false; }
}
function serviceVariantIsDiy(variant) {
  return !!variant && (
    variant.labelKey === 'svc.variant.diy'
    || variant.tier === 'DIY'
    || /_diy$/.test(String(variant.k || ''))
  );
}

function serviceVariantsForMode(variants, mode) {
  const rows = Array.isArray(variants) ? variants : [];
  if (mode === 'diy') return rows.filter(serviceVariantIsDiy);
  if (mode === 'professional') return rows.filter((variant) => !serviceVariantIsDiy(variant));
  return rows;
}

function ServiceCard({ s, openModal, forceUnowned, purchased, purchasedKeys, serviceMode = 'all' }) {
  const { t, lang, currency } = useT();
  const normalizeServiceKey = window.YEYE_PURCHASES && window.YEYE_PURCHASES.normalizeKey
    ? window.YEYE_PURCHASES.normalizeKey
    : (key) => String(key || '').trim().toLowerCase();
  const keyIsOwned = (key) => purchasedKeys && purchasedKeys.has(normalizeServiceKey(key));
  // Owned variants/tabs stay VISIBLE in the catalog family cards (a purchased Blue Card must not
  // vanish from the Work Permit card). Re-buying an owned variant is guarded by the rebuy warning.
  const allTabs = (Array.isArray(s.tabs) ? s.tabs : []).map((tab) => {
    const variants = Array.isArray(tab.variants) ? tab.variants : [];
    if (!variants.length) return serviceMode !== 'diy' ? tab : null;
    const availableVariants = serviceVariantsForMode(variants, serviceMode);
    return availableVariants.length ? { ...tab, variants: availableVariants } : null;
  }).filter(Boolean);
  const tabs = allTabs;
  const [selectedTab, setSelectedTab] = useState(tabs[0] && tabs[0].k);
  const soloVariants = serviceVariantsForMode(s.variants, serviceMode);
  const soloVariantHost = !s.family && soloVariants.length > 0 ? { ...s, variants: soloVariants } : null;
  const activeTab = tabs.find((tab) => tab.k === selectedTab) || tabs[0] || soloVariantHost;
  const [selectedVariant, setSelectedVariant] = useState(activeTab && activeTab.variants && activeTab.variants[0] && activeTab.variants[0].k);
  const activeComingSoon = !!(s.comingSoon || (activeTab && activeTab.comingSoon));
  const activeVariants = !activeComingSoon && activeTab
    ? serviceVariantsForMode(activeTab.variants, serviceMode)
    : [];
  useEffect(() => {
    if (!tabs.some((tab) => tab.k === selectedTab)) setSelectedTab(tabs[0] && tabs[0].k);
    setSelectedVariant(activeVariants[0] && activeVariants[0].k);
  }, [selectedTab, serviceMode]);
  const activeVariant = activeVariants.find((variant) => variant.k === selectedVariant) || activeVariants[0];
  const localizeAskForPrice = (v) => (v === 'Ask for price' ? t('svc.comingSoon') : v);
  const familyPrice = activeTab ? (activeComingSoon ? t('svc.comingSoon') : localizeAskForPrice(svcPrice({ k: (activeVariant && activeVariant.k) || activeTab.k, p: (activeVariant && activeVariant.price) || activeTab.price, priceLabel: (activeVariant && activeVariant.priceLabel) || activeTab.priceLabel, u: (activeVariant && activeVariant.u) || activeTab.u }, currency, t))) : '';
  const familyPriceLabel = (activeVariant && activeVariant.priceLabel) || (activeTab && activeTab.priceLabel);
  const price = localizeAskForPrice(svcPrice(s, currency, t));
  const owned = purchased || (s.owned && !forceUnowned);
  const title = svcName(s, t, lang);
  const catLabel = svcCatName(s, t);
  const handleNotifyMe = () => {
    openModal('ticket', { catK: 'sro', subject: t('services.notifySubject') + ': ' + title });
  };
  const handleFamilyReview = () => {
    if (!activeTab) return;
    if (activeComingSoon) {
      handleNotifyMe();
      return;
    }
    if (!s.family && activeVariant) {
      openModal('service', {
        ...s,
        k: activeVariant.k || s.k,
        parentK: s.k,
        p: activeVariant.price || s.p,
        priceLabel: activeVariant.priceLabel || s.priceLabel,
        u: activeVariant.u || s.u,
        payUrl: activeVariant.payUrl != null ? activeVariant.payUrl : s.payUrl,
        description: activeVariant.description || s.description,
        deliverables: activeVariant.deliverables || s.deliverables,
        notIncluded: activeVariant.notIncluded || s.notIncluded,
        cantPromise: activeVariant.cantPromise || s.cantPromise,
        _activeTabKey: s.k,
        _activeVariantKey: activeVariant.k,
      });
      return;
    }
    openModal('service', { ...s, _activeTabKey: activeTab.k, _activeVariantKey: activeVariant && activeVariant.k });
  };
  if ((s.family && tabs.length) || (soloVariantHost && activeVariants.length > 0)) {
    return (
      <Card className="card-pad svc-card">
        <header className="svc-card-head">
          <div className="hd-ic" style={{ width: 40, height: 40, borderRadius: 11, flexShrink: 0, background: owned ? 'var(--brand-50)' : 'var(--accent-50)', color: owned ? 'var(--brand-600)' : 'var(--accent-600)' }}><Icon name={P.catIcon[s.cat]} size={19} /></div>
          {activeComingSoon && <Badge tone="warn">{t('svc.comingSoon')}</Badge>}
          {owned && <Badge tone="ok" dot>{t('c.active')}</Badge>}
        </header>
        <div className="strong svc-card-title">{title}</div>
        <div className="svc-card-body">
          {s.family && tabs.length > 0 && (
            <Seg items={tabs.map((tab) => ({ value: tab.k, label: localizedText(tab.label, lang) || tab.k }))} value={activeTab.k} onChange={setSelectedTab} />
          )}
          {activeVariants.length > 1 && (
            <Seg items={activeVariants.map((variant) => ({ value: variant.k, label: variant.labelKey ? t(variant.labelKey) : (localizedText(variant.label, lang) || variant.k) }))} value={activeVariant && activeVariant.k} onChange={setSelectedVariant} />
          )}
          <div>
            <div className="strong tnum" style={{ fontSize: 18 }}>{familyPrice}</div>
            {activeTab.unitNote && <div className="dim" style={{ marginTop: 3, fontSize: 12 }}>{localizedText(activeTab.unitNote, lang)}</div>}
          </div>
        </div>
        <footer className="svc-card-footer">
          <span className="strong tnum" style={{ fontSize: 14 }}>{familyPrice}</span>
          {activeComingSoon ? (
            <Btn variant="primary" size="sm" icon="bell" onClick={handleNotifyMe}>{t('svc.notifyMe')}</Btn>
          ) : owned ? (
            <div className="flex ac gap-8">
              <Btn variant="ghost" size="sm" icon="search" onClick={handleFamilyReview}>{t('c.review')}</Btn>
              <Btn variant="primary" size="sm" icon="shopping-cart" onClick={handleFamilyReview}>{t('svc.buyAgain')}</Btn>
            </div>
          ) : (
            <Btn variant="primary" size="sm" icon="search" onClick={handleFamilyReview}>{t('c.review')}</Btn>
          )}
        </footer>
      </Card>
    );
  }
  return (
    <Card className="card-pad svc-card">
      <header className="svc-card-head">
        <div className="hd-ic" style={{ width: 40, height: 40, borderRadius: 11, flexShrink: 0, background: owned ? 'var(--brand-50)' : 'var(--accent-50)', color: owned ? 'var(--brand-600)' : 'var(--accent-600)' }}><Icon name={P.catIcon[s.cat]} size={19} /></div>
        {s.comingSoon && <Badge tone="warn">{t('svc.comingSoon')}</Badge>}
        {owned && <Badge tone="ok" dot>{t('c.active')}</Badge>}
      </header>
      <div className="svc-card-body">
        <div>
          <div className="strong svc-card-title">{title}</div>
          <div className="svc-card-cat">{catLabel}</div>
          {s.note && <div className="dim" style={{ fontSize: 12.5 }}>{localizedText(s.note, lang)}</div>}
        </div>
        <div>
          <div className="strong tnum" style={{ fontSize: 18 }}>{price}</div>
          {s.unitNote && <div className="dim" style={{ marginTop: 3, fontSize: 12 }}>{localizedText(s.unitNote, lang)}</div>}
        </div>
      </div>
      <footer className="svc-card-footer">
        <span className="strong tnum" style={{ fontSize: 14 }}>{price}</span>
        {s.comingSoon ? (
          <Btn variant="primary" size="sm" icon="bell" onClick={handleNotifyMe}>{t('svc.notifyMe')}</Btn>
        ) : owned ? (
          <div className="flex ac gap-8">
            <Btn variant="ghost" size="sm" icon="search" onClick={() => openModal('service', s)}>{t('c.review')}</Btn>
            <Btn variant="primary" size="sm" icon="shopping-cart" onClick={() => openModal('service', s)}>{t('svc.buyAgain')}</Btn>
          </div>
        ) : (
          <Btn variant="primary" size="sm" icon="search" onClick={() => openModal('service', s)}>{t('c.review')}</Btn>
        )}
      </footer>
    </Card>
  );
}

function serviceFocusKeys(s) {
  const keys = [s.k];
  if (s.family && Array.isArray(s.tabs)) {
    s.tabs.forEach((tab) => {
      keys.push(tab.k);
      if (Array.isArray(tab.variants)) tab.variants.forEach((variant) => keys.push(variant.k));
    });
  }
  return keys.join(' ');
}

function serviceSearchText(service, t, lang) {
  const values = [
    service && service.k,
    service && localizedText(service.name, lang),
    service && service.cat,
    service && svcCatName(service, t),
    ...([].concat((service && service.tags) || [], (service && service.categories) || [], (service && service.keywords) || [])),
  ];
  const options = service && service.family && Array.isArray(service.tabs) ? service.tabs : [service];
  options.filter(Boolean).forEach((option) => {
    values.push(option.k, localizedText(option.name, lang), localizedText(option.label, lang));
    (option.variants || []).forEach((variant) => {
      values.push(
        variant.k,
        localizedText(variant.name, lang),
        localizedText(variant.label, lang),
        variant.labelKey ? t(variant.labelKey) : ''
      );
    });
  });
  return values.filter(Boolean).join(' ').toLocaleLowerCase(lang);
}

function ServiceCategoryStrip({ cats, visibleServices, ownedKeys, t }) {
  const rows = (cats || []).map((cat) => {
    const count = visibleServices.filter((s) => s.cat === cat && !s.comingSoon).length;
    return { cat, count };
  }).filter((row) => row.count > 0);
  if (!rows.length) return null;
  return (
    <nav className="service-cat-strip" aria-label={t('c.availableServices')}>
      {rows.map(({ cat, count }) => (
        <a className="service-cat-chip" key={cat} href={'#services-cat-' + cat}>
          <Icon name={P.catIcon[cat]} size={15} />
          <span>{svcCatName({ cat }, t)}</span>
          <span className="dim mono">{count}</span>
        </a>
      ))}
    </nav>
  );
}

function purchasedServiceDetail(base, purchase, t, lang, currency) {
  const key = String((purchase && purchase.k) || (base && base.k) || '');
  const fallback = {
    title: svcName(base, t, lang),
    category: svcCatName(base, t),
    mode: '',
    price: svcPrice(base, currency, t),
  };
  if (!base) return fallback;
  if (!base.family && Array.isArray(base.variants)) {
    const picked = base.variants.find((variant) => variant.k === key || variant.serviceKey === key);
    if (!picked) return fallback;
    return {
      ...fallback,
      mode: localizedText(picked.label, lang) || t(picked.labelKey || '') || '',
      price: svcPrice({ ...base, k: picked.k || picked.serviceKey || base.k, p: picked.price || picked.priceLabel || base.p, priceLabel: picked.priceLabel || base.priceLabel, u: picked.u || base.u }, currency, t),
    };
  }
  if (!base.family || !Array.isArray(base.tabs)) return fallback;
  for (const tab of base.tabs) {
    const variants = Array.isArray(tab.variants) ? tab.variants : [];
    const picked = variants.find((variant) => variant.k === key) || (tab.k === key ? variants[0] : null);
    if (!picked) continue;
    return {
      title: localizedText(tab.label, lang) || fallback.title,
      category: fallback.title,
      mode: localizedText(picked.label, lang) || t(picked.labelKey || '') || '',
      price: svcPrice({ ...base, k: picked.k || tab.k || base.k, p: picked.price || tab.price || base.p, priceLabel: picked.priceLabel || tab.priceLabel || base.priceLabel, u: picked.u || tab.u || base.u }, currency, t),
    };
  }
  return fallback;
}

function catalogServiceForPurchase(key) {
  const raw = String(key || '');
  if (raw === 'accommodation' || raw === 'accommodation_confirmation') {
    return (P.services || []).find((service) => service.k === 'accommodation_family')
      || (P.services || []).find((service) => service.k === 'accom12')
      || null;
  }
  const normalize = typeof window.YEYE_NORMALIZE_SERVICE_KEY === 'function' ? window.YEYE_NORMALIZE_SERVICE_KEY : (value) => value;
  const normalized = normalize(raw);
  for (const service of (P.services || [])) {
    const keys = [service.k];
    if (service.family && Array.isArray(service.tabs)) {
      service.tabs.forEach((tab) => {
        keys.push(tab.k);
        if (Array.isArray(tab.variants)) tab.variants.forEach((variant) => keys.push(variant.k));
      });
    }
    if (keys.some((item) => item === raw || normalize(item) === normalized)) return service;
  }
  return null;
}

function isDiyPurchasedService(purchase) {
  if (!purchase) return false;
  const key = String(purchase.k || purchase.key || purchase.serviceKey || purchase.productKey || '').toLowerCase();
  const name = localizedText(purchase.name, 'en') || purchase.serviceName || purchase.title || '';
  if (purchase.tier === 'DIY-Docs' || /_diy$/.test(key) || /\s—\sDIY$/i.test(String(name))) return true;
  for (const service of (P.services || [])) {
    const options = service.family && Array.isArray(service.tabs) ? service.tabs : [service];
    for (const option of options) {
      if (option.k === key && (option.tier === 'DIY-Docs' || /_diy$/.test(String(option.k || '')) || /\s—\sDIY$/i.test(String(localizedText(option.name, 'en') || option.label || '')))) return true;
      const variant = (option.variants || []).find((item) => String(item.k || '').toLowerCase() === key);
      if (variant && (variant.tier === 'DIY-Docs' || variant.tier === 'DIY' || variant.labelKey === 'svc.variant.diy' || /_diy$/.test(String(variant.k || '')))) return true;
    }
  }
  return false;
}
window.YEYE_IS_DIY_PURCHASE = isDiyPurchasedService;

function PurchasedServiceSummary({ service, purchase, openModal }) {
  const { t, lang, currency } = useT();
  const detail = purchasedServiceDetail(service, purchase, t, lang, currency);
  const status = (purchase && purchase.status) || 'active';
  return (
    <div className="owned-service-card">
      <div className="owned-service-accent" />
      <div className="owned-service-top">
        <div className="owned-service-icon"><Icon name={P.catIcon[service.cat] || 'package-check'} size={19} /></div>
        <Badge tone={status === 'cancelled' ? 'bad' : status === 'paused' ? 'warn' : 'ok'} dot>{t('st.' + status)}</Badge>
      </div>
      <div className="owned-service-body">
        <div className="eyebrow">{detail.category}</div>
        <h3>{detail.title}</h3>
        {detail.mode && <div className="owned-service-mode">{detail.mode}</div>}
      </div>
      <div className="owned-service-foot">
        <strong className="tnum">{detail.price}</strong>
        <Btn variant="ghost" size="sm" icon="settings" onClick={() => openModal('manageService', { purchase, service })}>{t('c.manage')}</Btn>
      </div>
    </div>
  );
}

function ServicesPage({ openModal, purchasedServices, blankProfile }) {
  const { t, lang } = useT();
  const [serviceFilter, setServiceFilter] = useState(() => {
    const requested = new URLSearchParams(window.location.search || '').get('filter');
    return ['all', 'professional', 'diy'].includes(requested) ? requested : 'all';
  });
  const [searchInput, setSearchInput] = useState('');
  const [serviceSearch, setServiceSearch] = useState('');
  useEffect(() => {
    const timer = window.setTimeout(() => setServiceSearch(searchInput.trim().toLocaleLowerCase(lang)), 200);
    return () => window.clearTimeout(timer);
  }, [searchInput, lang]);
  const fresh = blankProfile || isFreshUserEmail();
  const normalizeServiceKey = window.YEYE_PURCHASES && window.YEYE_PURCHASES.normalizeKey
    ? window.YEYE_PURCHASES.normalizeKey
    : (key) => String(key || '').trim().toLowerCase();
  const purchasedKeys = new Set((purchasedServices || []).map((x) => normalizeServiceKey(x.k)));
  const familyKeys = (s) => {
    const keys = [s.k];
    if (s.family && Array.isArray(s.tabs)) {
      s.tabs.forEach((tab) => {
        keys.push(tab.k);
        if (Array.isArray(tab.variants)) tab.variants.forEach((variant) => keys.push(variant.k));
      });
    }
    if (!s.family && Array.isArray(s.variants)) s.variants.forEach((variant) => keys.push(variant.k));
    return keys.map((key) => normalizeServiceKey(key));
  };
  const purchaseForService = (s) => {
    const keys = new Set(familyKeys(s));
    return (purchasedServices || []).find((row) => keys.has(normalizeServiceKey(row.k))) || null;
  };
  const isOwned = (s) => !!purchaseForService(s) || (!fresh && s.owned);
  const purchasableKeys = (s) => {
    if (s.family && Array.isArray(s.tabs)) {
      return s.tabs.flatMap((tab) => Array.isArray(tab.variants) && tab.variants.length
        ? tab.variants.map((variant) => normalizeServiceKey(variant.k))
        : [normalizeServiceKey(tab.k)]);
    }
    if (Array.isArray(s.variants) && s.variants.length) return s.variants.map((variant) => normalizeServiceKey(variant.k));
    return [normalizeServiceKey(s.k)];
  };
  const isFullyOwned = (s) => {
    if (!isOwned(s)) return false;
    if (!fresh && s.owned) return true;
    const keys = purchasableKeys(s).filter(Boolean);
    const parentOwned = purchasedKeys.has(normalizeServiceKey(s.k)) && !keys.includes(normalizeServiceKey(s.k));
    return parentOwned || (keys.length > 0 && keys.every((key) => purchasedKeys.has(key)));
  };
  const serviceHasMode = (service, mode) => {
    const options = service.family && Array.isArray(service.tabs) ? service.tabs : [service];
    const variants = options.flatMap((option) => Array.isArray(option.variants) ? option.variants : []);
    if (variants.length) return serviceVariantsForMode(variants, mode).some((variant) => !purchasedKeys.has(normalizeServiceKey(variant.k)));
    return mode === 'professional' && options.some((option) => !purchasedKeys.has(normalizeServiceKey(option.k))) && service.tier !== 'DIY' && !String(service.k || '').endsWith('_diy');
  };
  const changeServiceFilter = (nextFilter) => {
    setServiceFilter(nextFilter);
    const params = new URLSearchParams(window.location.search || '');
    if (nextFilter === 'all') params.delete('filter');
    else params.set('filter', nextFilter);
    const query = params.toString();
    try { window.history.replaceState({}, '', window.location.pathname + (query ? '?' + query : '') + window.location.hash); } catch (_) {}
  };
  const visibleServices = P.services.filter((s) => (
    !s.hiddenFromGrid
    && (serviceFilter === 'all' || serviceHasMode(s, serviceFilter) || isFullyOwned(s))
    && (!serviceSearch || serviceSearchText(s, t, lang).includes(serviceSearch))
  ));
  const owned = visibleServices.filter((s) => isFullyOwned(s));
  const ownedKeys = new Set(owned.map((s) => s.k));
  const availableServices = visibleServices.filter((s) => !s.comingSoon);
  const availableCount = availableServices.length;
  const comingSoonCount = visibleServices.filter((s) => s.comingSoon).length;
  return (
    <>
      <div className="services-filter mb-20">
        <Seg items={[
          { value: 'all', label: t('services.filter.all') },
          { value: 'professional', label: t('services.filter.professional') },
          { value: 'diy', label: t('services.filter.diy') },
        ]} value={serviceFilter} onChange={changeServiceFilter} />
      </div>
      <div className="services-search mb-20">
        <Icon name="search" size={18} />
        <input
          type="search"
          value={searchInput}
          onChange={(event) => setSearchInput(event.target.value)}
          placeholder={t('services.searchPlaceholder')}
          aria-label={t('services.searchPlaceholder')}
        />
        {searchInput && (
          <button type="button" className="icon-btn" onClick={() => setSearchInput('')} aria-label={t('services.searchClear')}>
            <Icon name="x" size={17} />
          </button>
        )}
      </div>
      <ServiceCategoryStrip cats={P.serviceCats} visibleServices={visibleServices} ownedKeys={ownedKeys} t={t} />
      {/* Purchased/owned services stay VISIBLE in the catalog grid (marked as
          Active with a "Buy again" CTA); they also appear on the dedicated
          Purchased Services page. */}
      <div className="flex ac gap-8 mb-16 wrap">
        <div className="eyebrow">{t('c.availableServices')}</div>
        <Badge tone="neut">{availableCount}</Badge>
        {comingSoonCount > 0 && <span className="dim" style={{ fontSize: 12.5 }}>{t('svc.comingSoonCount').replace('{n}', comingSoonCount)}</span>}
      </div>
      {serviceSearch && visibleServices.length === 0 && (
        <div className="services-search-empty">
          <Icon name="search-x" size={22} />
          <span>{t('services.searchEmpty')}</span>
        </div>
      )}
      {P.serviceCats.map((cat) => {
        const allInCat = visibleServices.filter((s) => s.cat === cat);
        const catComingSoon = allInCat.length > 0 && allInCat.every((s) => s.comingSoon);
        if (catComingSoon) {
          return (
            <div key={cat} id={'services-cat-' + cat} className="mb-24">
              <div className="flex ac gap-10 mb-12">
                <div className="hd-ic" style={{ width: 30, height: 30, borderRadius: 8 }}><Icon name={P.catIcon[cat]} size={16} /></div>
                <div className="h3" style={{ whiteSpace: 'nowrap', flexShrink: 0 }}>{svcCatName({ cat }, t)}</div>
              </div>
              <div className="flex ac jc gap-10" style={{ padding: '28px 20px' }}>
                <Icon name="clock" size={18} />
                <div className="h4" style={{ margin: 0 }}>{t('svc.comingSoon')}</div>
              </div>
            </div>
          );
        }
        const list = allInCat
          .filter((s) => !s.comingSoon)
          .map((s, i) => ({ s, i }))
          .sort((a, b) => {
            const ar = Number.isFinite(a.s.sortRank) ? a.s.sortRank : (a.s.family ? (a.s.eyebrow === 'most_requested' ? 0 : 1) : 3);
            const br = Number.isFinite(b.s.sortRank) ? b.s.sortRank : (b.s.family ? (b.s.eyebrow === 'most_requested' ? 0 : 1) : 3);
            return ar - br || a.i - b.i;
          })
          .map((row) => row.s);
        if (!list.length) return null;
        return (
          <div key={cat} id={'services-cat-' + cat} className="mb-24">
            <div className="flex ac gap-10 mb-12">
              <div className="hd-ic" style={{ width: 30, height: 30, borderRadius: 8 }}><Icon name={P.catIcon[cat]} size={16} /></div>
              <div style={{ minWidth: 0 }}>
                <div className="h3">{svcCatName({ cat }, t)}</div>
                {P.catDescriptions && P.catDescriptions[cat] && <div className="dim" style={{ fontSize: 12.5 }}>{localizedText(P.catDescriptions[cat], lang)}</div>}
              </div>
              <span className="dim" style={{ fontSize: 12.5 }}>{list.length}</span>
            </div>
            <div className="grid g-12 svc-grid" style={{ gap: 16 }}>
              {list.map((s) => <div className="col-3" key={s.k} data-service-keys={serviceFocusKeys(s)}><ServiceCard s={s} openModal={openModal} forceUnowned={fresh} purchased={purchasedKeys.has(normalizeServiceKey(s.k))} purchasedKeys={purchasedKeys} serviceMode={serviceFilter} /></div>)}
            </div>
          </div>
        );
      })}
    </>
  );
}

function PurchasedServicesPage({ purchasedServices, go, blankProfile }) {
  const { t } = useT();
  const rows = (purchasedServices || []).filter((row) => row && row.status !== 'cancelled');
  const cart = useCartState();
  const hasDiyPurchase = rows.some(isDiyPurchasedService);
  const normalizeServiceKey = typeof window.YEYE_NORMALIZE_SERVICE_KEY === 'function' ? window.YEYE_NORMALIZE_SERVICE_KEY : (key) => key;
  const processSeen = new Set();
  const ownedProcessRows = (blankProfile ? [] : (P.services || []).filter((service) => service.owned).map((service) => ({ service, purchase: null })));
  const playbookClient = window.YEYE_PLAYBOOK_CLIENT;
  const processApplications = rows.map((row) => ({
    service: catalogServiceForPurchase(row.k) || row,
    purchase: row,
  })).concat(ownedProcessRows).map(({ service, purchase }) => {
      const rawKey = String((purchase && purchase.k) || service.k || '').toLowerCase();
      // Resolve through the same playbook path the dashboard uses, so every
      // purchased row that the dashboard can render also renders here — and Pro
      // vs DIY of the same service stay as two separate cards, each carrying the
      // correct mode suffix ("· Do With YeYe" / "· Do It Yourself"). The old
      // hand-rolled processType map dropped rows (e.g. emp_card_diy fell through
      // to the legacy tracker and rendered nothing) and lost the DIY label.
      const resolveInput = purchase || { k: service.k };
      const match = playbookClient && typeof playbookClient.resolvePlaybookPurchase === 'function'
        ? playbookClient.resolvePlaybookPurchase(resolveInput, '')
        : null;
      const serviceId = match && match.serviceId;
      if (!serviceId) return null;
      const isDiy = !!(match.isDiy || /_diy$/.test(rawKey) || isDiyPurchasedService(purchase));
      const dedupKey = `${serviceId}:${isDiy ? 'diy' : 'pro'}`;
      if (processSeen.has(dedupKey)) return null;
      processSeen.add(dedupKey);
      return { key: serviceId, renderKey: dedupKey, labelKey: service.k, purchase };
    })
    .filter(Boolean);
  return (
    <>
      <PageHead title={t('nav.purchasedServices')} sub={t('sub.purchasedServices')}
        actions={<Btn variant="primary" icon="grid-2x2" onClick={() => go('services')}>{t('nav.services')}</Btn>} />
      {hasDiyPurchase && <div className="mb-20"><DiyPrecheckNudge cartItems={cart.items} hasDiyPurchase /></div>}
      {processApplications.length === 0 && (
        <div className="empty-state mb-20">
          <Icon name="shopping-bag" size={22} />
          <span>{t('myData.internalForm.emptyBody')}</span>
        </div>
      )}
      {processApplications.length > 0 && (
        <div className="mb-20">
          <div className="flex ac gap-8 mb-12"><div className="eyebrow">{t('dash.applications')}</div><Badge tone="info">{processApplications.length}</Badge></div>
          <div className="grid g-12" style={{ gap: 16 }}>
            {processApplications.map((app) => (
              <div className="col-12" key={app.renderKey || app.key}>
                <ProcessTrackerCard ct={{ key: app.key }} purchase={app.purchase} />
              </div>
            ))}
          </div>
        </div>
      )}
    </>
  );
}

const DOCGEN_LIBRARY_TEMPLATES = {
  application: {
    labelKey: 'docgen.doc.application', descKey: 'docgen.doc.application.desc', icon: 'file-text',
    documentKey: 'application_form',
    templateId: (serviceForm) => serviceForm === 'employee_card' ? '1MxVyb3_4epmtxYA91DSccnlDxtY0ByMC' : 'application_form',
    fillFormType: (serviceForm) => serviceForm === 'employee_card' ? 'employee_card_form' : serviceForm,
    fieldForm: (serviceForm) => serviceForm === 'employee_card' ? 'employee_card' : serviceForm,
    match: (file, serviceForm) => {
      const text = `${file.name || file.fileName || file.title || ''} ${file.url || file.webViewLink || ''}`.toLowerCase();
      if (serviceForm === 'employee_card') return /employee.*card|1yrdc1kfkr1es2pwcveu1rbzdeX2lq2du/i.test(text);
      return /blue.*card|1bzga_fxl0nqzykgw2_lyzfmm_ahff9r3/i.test(text);
    },
  },
  'work-contract': {
    labelKey: 'docgen.doc.workContract', descKey: 'docgen.doc.workContract.desc', icon: 'briefcase-business',
    documentKey: 'work_contract', templateId: 'truck_driver_contract', processType: 'work_contract',
    fillFormType: () => 'employee_onboarding', fieldForm: () => 'employee_onboarding',
    match: (file) => /pracovni|pracovní|employment contract|work contract|smlouva/i.test(String(file.name || file.fileName || file.title || '')),
  },
  salary: {
    labelKey: 'docgen.doc.salary', descKey: 'docgen.doc.salary.desc', icon: 'badge-dollar-sign',
    documentKey: 'salary_certificate', templateId: 'salary_certificate', processType: 'salary_certificate',
    fillFormType: () => 'employee_onboarding', fieldForm: () => 'employee_onboarding',
    match: (file) => /mzdov|mzda|wage|salary/i.test(String(file.name || file.fileName || file.title || '')),
  },
  poa: {
    labelKey: 'docgen.doc.poa', descKey: 'docgen.doc.poa.desc', icon: 'stamp',
    documentKey: 'power_of_attorney_salih', templateId: 'poa_salih', processType: 'power_of_attorney_salih',
    fillFormType: () => 'employee_onboarding', fieldForm: () => 'employee_onboarding',
    match: (file) => /plna moc|plná moc|power of attorney|poa/i.test(String(file.name || file.fileName || file.title || '')),
  },
  accommodation: {
    labelKey: 'docgen.doc.accommodation', descKey: 'docgen.doc.accommodation.desc', icon: 'house',
    documentKey: 'accommodation_confirmation', templateId: 'accommodation_confirmation', processType: 'accommodation_confirmation',
    fillFormType: () => 'employee_onboarding', fieldForm: () => 'employee_onboarding',
    match: (file) => /accommodation|ubytovani|ubytování/i.test(String(file.name || file.fileName || file.title || '')),
  },
};

const DOCGEN_EXPLICIT_TEMPLATE_IDS = new Set(['1MxVyb3_4epmtxYA91DSccnlDxtY0ByMC']);
function docgenExplicitTemplateId(value) {
  const id = String(value || '').trim();
  return DOCGEN_EXPLICIT_TEMPLATE_IDS.has(id);
}

function docgenCardFormType(rawKey, normalizedKey, declaredFormType) {
  const raw = String(rawKey || '').toLowerCase();
  const normalized = String(normalizedKey || '').toLowerCase();
  const declared = String(declaredFormType || '').toLowerCase();
  if (['blue_card', 'blue_card_diy', 'blue_card_ext', 'blue_card_ext_diy'].includes(raw) || ['blue_card', 'blue_card_ext'].includes(normalized)) return 'blue_card';
  if ([
    'employee_card', 'employee_card_diy', 'employee_card_ext', 'employee_card_ext_diy',
    'emp_card', 'emp_card_diy', 'emp_card_ext', 'emp_card_ext_diy',
  ].includes(raw) || ['employee_card', 'employee_card_ext', 'emp_card', 'emp_card_ext'].includes(normalized)) return 'employee_card';
  if (declared === 'employee_card_form') return 'employee_card';
  if (['blue_card', 'employee_card'].includes(declared)) return declared;
  return '';
}

const DOCGEN_PERSON_REQUIRED_FIELDS = [
  'application_type', 'appType', 'first_name', 'firstName', 'last_name', 'lastName', 'email', 'phone',
  'date_of_birth', 'dateOfBirth', 'place_of_birth', 'country_of_birth', 'empccountryofbirth_country_list',
  'nationality', 'nationality_country_list', 'gender', 'marital_status', 'occupation', 'highest_education',
  'numChildren', 'how_many_children_do_you_have', 'numSiblings', 'how_many_siblings_do_you_have',
];
const DOCGEN_PASSPORT_REQUIRED_FIELDS = [
  'passport_number', 'passport_date_of_issue', 'passport_expiry_date', 'passport_expiry',
  'passport_place_of_issue', 'passport_country_of_issue', 'passport_country_of_issue_country_list',
];
const DOCGEN_RESIDENCE_REQUIRED_FIELDS = [
  'resCountry', 'country_residence', 'country_residence_country_list',
  'resMunicipality', 'municipality_in_residence', 'resDistrict', 'district',
  'resStreetNumber', 'street_number', 'resBuildingNumber', 'building_no', 'resPostalCode', 'zip_code',
];
const DOCGEN_CZECHIA_ADDRESS_REQUIRED_FIELDS = [
  'municipality_in_czechia', 'district_in_czechia', 'street_number_in_czechia',
  'building_number_in_czechia', 'postalzip_code_in_czechia', 'arrival_date',
];
const DOCGEN_EMPLOYMENT_REQUIRED_FIELDS = [
  'cpcomanyname', 'company_name', 'employer_name', 'contactemployee_job_position', 'job_position',
  'company_address_country', 'company_address_country_country_list', 'company_address_municipality',
  'company_address_district', 'company_address_street', 'company_address_building_number',
  'company_address_postal_zip_code',
  'employee_contract_start_date', 'contract_start', 'employee_contract_end_date', 'contract_end',
  'contactemployee_work_address', 'place_of_work', 'salary',
];
const DOCGEN_CARD_REQUIRED_FIELDS = [
  ...DOCGEN_PERSON_REQUIRED_FIELDS,
  ...DOCGEN_PASSPORT_REQUIRED_FIELDS,
  ...DOCGEN_RESIDENCE_REQUIRED_FIELDS,
  ...DOCGEN_CZECHIA_ADDRESS_REQUIRED_FIELDS,
  ...DOCGEN_EMPLOYMENT_REQUIRED_FIELDS,
];
const DOCGEN_EMPLOYEE_ONBOARDING_REQUIRED_FIELDS = DOCGEN_CARD_REQUIRED_FIELDS;

const DOCGEN_HARD_REQUIRED_FIELDS = {
  blue_card: DOCGEN_CARD_REQUIRED_FIELDS,
  blue_card_form: DOCGEN_CARD_REQUIRED_FIELDS,
  employee_card: DOCGEN_CARD_REQUIRED_FIELDS,
  employee_card_form: DOCGEN_CARD_REQUIRED_FIELDS,
  employee_onboarding: DOCGEN_EMPLOYEE_ONBOARDING_REQUIRED_FIELDS,
  application: DOCGEN_CARD_REQUIRED_FIELDS,
  application_form: DOCGEN_CARD_REQUIRED_FIELDS,
  'work-contract': ['first_name', 'last_name', ...DOCGEN_PASSPORT_REQUIRED_FIELDS, ...DOCGEN_EMPLOYMENT_REQUIRED_FIELDS],
  work_contract: ['first_name', 'last_name', ...DOCGEN_PASSPORT_REQUIRED_FIELDS, ...DOCGEN_EMPLOYMENT_REQUIRED_FIELDS],
  salary: ['first_name', 'last_name', ...DOCGEN_EMPLOYMENT_REQUIRED_FIELDS],
  salary_certificate: ['first_name', 'last_name', ...DOCGEN_EMPLOYMENT_REQUIRED_FIELDS],
  poa: ['first_name', 'last_name', 'date_of_birth', 'place_of_birth', 'nationality', ...DOCGEN_PASSPORT_REQUIRED_FIELDS],
  power_of_attorney: ['first_name', 'last_name', 'date_of_birth', 'place_of_birth', 'nationality', ...DOCGEN_PASSPORT_REQUIRED_FIELDS],
  power_of_attorney_salih: ['first_name', 'last_name', 'date_of_birth', 'place_of_birth', 'nationality', ...DOCGEN_PASSPORT_REQUIRED_FIELDS],
  accommodation: ['first_name', 'last_name', 'date_of_birth', ...DOCGEN_CZECHIA_ADDRESS_REQUIRED_FIELDS],
  accommodation_confirmation: ['first_name', 'last_name', 'date_of_birth', ...DOCGEN_CZECHIA_ADDRESS_REQUIRED_FIELDS],
};

function normalizeDocgenFieldKey(value) {
  return String(value || '')
    .replace(/^(?:contact|business)[.\s_-]+/i, '')
    .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, '_')
    .replace(/^_+|_+$/g, '');
}

function docgenRowIdentifiers(row) {
  return [row?.fieldName, row?.fieldKey, row?.key, row?.formKey, row?.updateKey]
    .filter(Boolean)
    .map(normalizeDocgenFieldKey);
}

function docgenHasValue(value) {
  if (Array.isArray(value)) return value.some(docgenHasValue);
  if (value === undefined || value === null) return false;
  const normalized = String(value).trim().toLowerCase();
  return normalized !== '' && normalized !== '—' && normalized !== 'eksik';
}

function filterDocgenRequiredRows(rows, documentSlug) {
  const knownDocumentSlug = Object.prototype.hasOwnProperty.call(DOCGEN_HARD_REQUIRED_FIELDS, documentSlug);
  const hardRequired = new Set((DOCGEN_HARD_REQUIRED_FIELDS[documentSlug] || []).map(normalizeDocgenFieldKey));
  return (rows || []).filter((row) => {
    const missing = ![row?.value, row?.v].some(docgenHasValue);
    const required = !knownDocumentSlug || row?.required === true || docgenRowIdentifiers(row).some((key) => hardRequired.has(key));
    return missing && required;
  });
}

function inferDocgenSchemaSectionId(row, schemaRows) {
  const canonicalSectionId = window.YEYE_FIELD_MAPPING?.sectionIdFor?.(row);
  if (canonicalSectionId) return canonicalSectionId;
  const wanted = new Set(docgenRowIdentifiers(row));
  const match = (schemaRows || []).find((schemaRow) => docgenRowIdentifiers(schemaRow).some((key) => wanted.has(key)));
  return String(match?.sectionId || row?.sectionId || '');
}

function docgenSectionGroup(sectionId) {
  const groups = {
    contact_kisisel_iletisim: 'contact',
    personal_information: 'personal',
    company: 'company',
    eob_calisma_izin: 'company',
    last_employment: 'lastEmployment',
    residence_abroad: 'residence',
    czechia_address: 'czechia',
    shipping_address_shippingsameasczechia_no_ise_gosterilir: 'shipping',
    passport_travel_document: 'passport',
    spouse_maritalstatus_married_ise: 'family',
    children_1_4_dinamik: 'family',
    parents: 'family',
    siblings_1_4_dinamik: 'family',
  };
  return groups[sectionId] || 'other';
}

function groupDocgenRows(rows, schemaRows, t) {
  const groups = new Map();
  (rows || []).forEach((row) => {
    const schemaSectionId = inferDocgenSchemaSectionId(row, schemaRows);
    const group = docgenSectionGroup(schemaSectionId);
    if (!groups.has(group)) groups.set(group, { id: group, schemaSectionId, title: t(`docgen.section.${group}`), rows: [] });
    groups.get(group).rows.push({ ...row, __docgenSectionId: schemaSectionId });
  });
  return Array.from(groups.values());
}

function docgenFiles(payload) {
  return [].concat(payload?.documents || [], payload?.generated || [], payload?.generatedDocs || [], payload?.files || [])
    .flatMap((file) => file?.files?.length ? file.files : [file])
    .filter(Boolean);
}

function normalizeDocgenResult(payload, doc, base) {
  const files = docgenFiles(payload);
  const matchedFile = files.find((file) => docgenFileMatches(file, doc)) ||
    files.find((file) => doc.match && doc.match(file, doc.serviceFormType)) ||
    files.find((file) => file.pdfUrl || file.downloadUrl || file.url || file.webViewLink || file.webContentLink) ||
    {};
  const pdfUrls = [].concat(matchedFile.pdfUrl || [], matchedFile.pdfUrls || [], payload.pdfUrls || [], payload.pdfUrl || []).filter(Boolean);
  const payloadPdfUrl = [].concat(payload.pdfUrls || []).filter(Boolean)[0] || '';
  const documentUrl = matchedFile.pdfUrl || matchedFile.downloadUrl || matchedFile.url || matchedFile.webViewLink || matchedFile.webContentLink ||
    payloadPdfUrl || payload.downloadUrl || payload.documentUrl || '';
  const documentId = matchedFile.pdfId || matchedFile.id || matchedFile.fileId || driveFileId(documentUrl) || payload.documentId || payload.primaryFileId || payload.pdfId || '';
  const directDownloadUrl = matchedFile.pdfUrl || matchedFile.downloadUrl || payload.downloadUrl || '';
  return {
    ...payload,
    pdfUrls,
    documentUrl,
    previewUrl: docgenPreviewUrl(documentUrl, documentId, base),
    downloadUrl: directDownloadUrl || (documentId && base ? `${base}/public/download/${encodeURIComponent(documentId)}` : documentUrl),
    downloadDocxUrl: documentId && base ? `${base}/public/download/${encodeURIComponent(documentId)}?format=docx` : '',
    folderUrl: payload.folderUrl || matchedFile.folderUrl || (payload.folder && payload.folder.url) || '',
    fileName: matchedFile.name || matchedFile.fileName || payload.fileName || '',
    placeholdersReplaced: payload.placeholdersReplaced ?? matchedFile.placeholdersReplaced ?? (payload.documents && payload.documents[0] && payload.documents[0].placeholdersReplaced) ?? 0,
  };
}

function docgenFileMatches(file, doc) {
  if (!file || !doc) return false;
  const haystack = [
    file.documentKey,
    file.document_key,
    file.templateId,
    file.template_id,
    file.type,
    file.key,
    file.name,
    file.fileName,
    file.title,
    file.url,
    file.webViewLink,
    file.pdfUrl,
  ].filter(Boolean).join(' ').toLowerCase();
  return [doc.documentKey, doc.templateId, doc.slug]
    .filter(Boolean)
    .some((value) => haystack.includes(String(value).toLowerCase().replace(/^tpl_/, '')));
}

function appendPdfHash(url) {
  const raw = String(url || '');
  return raw && !raw.includes('#') ? `${raw}#toolbar=0&navpanes=0&scrollbar=1` : raw;
}

function docgenPreviewUrl(url, fileId, baseUrl) {
  const raw = String(url || '');
  if (!raw) return '';
  if (/\.pdf(?:$|[?#])/i.test(raw)) return appendPdfHash(raw);
  if (fileId && baseUrl && /(drive|docs)\.google\.com/i.test(raw)) {
    return appendPdfHash(`${String(baseUrl).replace(/\/$/, '')}/public/download/${encodeURIComponent(fileId)}?inline=1`);
  }
  if (/drive\.google\.com\/file\/d\//i.test(raw)) return raw.replace(/\/view(?:\?.*)?$/, '/preview');
  if (/docs\.google\.com\/document\/d\/[^/]+\/edit/i.test(raw)) return raw.replace(/\/edit(?:[?#].*)?$/i, '/preview');
  return raw;
}

const DOCGEN_DRAFT_CACHE = (() => {
  const LS_KEY_PREFIX = 'yeye_draft_cache_';
  return {
    _mem: new Map(),
    has(k) { return this._mem.has(k) || !!localStorage.getItem(LS_KEY_PREFIX + k); },
    get(k) {
      if (this._mem.has(k)) return this._mem.get(k);
      try { const v = localStorage.getItem(LS_KEY_PREFIX + k); if (v) { const p = JSON.parse(v); this._mem.set(k, p); return p; } } catch (_) {}
      return undefined;
    },
    set(k, v) { this._mem.set(k, v); try { localStorage.setItem(LS_KEY_PREFIX + k, JSON.stringify(v)); } catch (_) {} },
    delete(k) { this._mem.delete(k); try { localStorage.removeItem(LS_KEY_PREFIX + k); } catch (_) {} },
  };
})();

function docgenDraftExplainerStorageKey(email) {
  const normalized = String(email || '').trim().toLowerCase();
  if (!normalized) return 'yeye_draft_explainer_dismissed_v1';
  let hash = 2166136261;
  for (let index = 0; index < normalized.length; index += 1) {
    hash ^= normalized.charCodeAt(index);
    hash = Math.imul(hash, 16777619);
  }
  return `yeye_draft_explainer_dismissed_v1_${(hash >>> 0).toString(36)}`;
}

function docgenConfirmedFieldKey(doc) {
  if (!doc) return '';
  if (doc.slug === 'application') {
    if (doc.serviceFormType === 'blue_card') return 'blue_card_application_form_url';
    if (doc.serviceFormType === 'employee_card') return 'employee_card_application_form_url';
    return '';
  }
  return {
    'work-contract': 'employment_contract_url',
    salary: 'salary_certificate_url',
    poa: 'power_of_attorney_url',
    accommodation: 'accommodation_confirmation_url',
  }[doc.slug] || '';
}

function docgenProcessTypeForDocumentKey(docKey, fallback = '') {
  return ({
    employment_contract: 'work_contract', work_contract: 'work_contract',
    salary_document: 'salary_certificate', salary_certificate: 'salary_certificate',
    power_of_attorney: 'power_of_attorney', power_of_attorney_salih: 'power_of_attorney_salih',
    accommodation_paper: 'accommodation_confirmation', accommodation_confirmation: 'accommodation_confirmation',
  })[String(docKey || '').trim()] || fallback;
}

function docgenDraftFileId(file) {
  return file?.id || file?.fileId || file?.file_id || file?.driveFileId || file?.drive_file_id || '';
}

function docgenDraftFileName(file) {
  return file?.name || file?.fileName || file?.file_name || file?.title || '';
}

function docgenDraftFileUrl(file) {
  return file?.webViewLink || file?.web_view_link || file?.driveUrl || file?.drive_url || file?.url || file?.documentUrl || '';
}

function docgenDraftPromotion(file) {
  const promotion = file?.promotedDocument || file?.promoted_document || file?.promotion || {};
  const status = String(file?.status || promotion?.status || '').toLowerCase();
  const docId = file?.promotedDocumentId || file?.promoted_document_id || file?.promotedDocId ||
    promotion?.id || promotion?.doc_id || promotion?.documentId || promotion?.document_id || '';
  const promoted = file?.promoted === true || file?.isPromoted === true || file?.is_promoted === true ||
    file?.confirmed === true || ['promoted', 'confirmed'].includes(status) || !!docId;
  return {
    promoted,
    docId: docId || (promoted ? (file?.doc_id || file?.documentId || file?.document_id || '') : ''),
    docKey: file?.promotedDocumentKey || file?.promoted_document_key || promotion?.docKey ||
      promotion?.doc_key || promotion?.documentKey || promotion?.document_key || file?.docKey || file?.documentKey || '',
  };
}

function docgenApprovalDate(file) {
  const promotion = file?.promotedDocument || file?.promoted_document || file?.promotion || {};
  const raw = file?.promotedAt || file?.promoted_at || file?.approvedAt || file?.approved_at ||
    promotion?.promotedAt || promotion?.promoted_at || promotion?.approvedAt || promotion?.approved_at ||
    promotion?.createdAt || promotion?.created_at || '';
  if (!raw) return '';
  const date = new Date(raw);
  if (Number.isNaN(date.getTime())) return '';
  try { return new Intl.DateTimeFormat(undefined, { year: 'numeric', month: 'short', day: 'numeric' }).format(date); } catch (_) { return date.toLocaleDateString(); }
}

function docgenDraftMetadata(file) {
  const promotion = docgenDraftPromotion(file);
  const appProps = file?.appProperties || file?.app_properties || {};
  return [
    appProps.yeye_document_key,
    appProps.yeye_process_type,
    promotion.docKey,
    file?.docKey,
    file?.doc_key,
    file?.documentKey,
    file?.document_key,
    file?.templateId,
    file?.template_id,
    file?.templateName,
    file?.template_name,
    file?.template,
    file?.type,
    docgenDraftFileName(file),
  ].filter(Boolean).join(' ');
}

function docgenDraftDocument(file, documents, allowSingleFallback = true) {
  const docs = (documents || []).filter(Boolean);
  const metadata = normalizeDocgenFieldKey(docgenDraftMetadata(file));
  const exact = docs.find((doc) => [doc.documentKey, doc.templateId, doc.slug]
    .filter(Boolean)
    .map(normalizeDocgenFieldKey)
    .some((key) => key && (metadata === key || metadata.split('_').join(' ').includes(key.split('_').join(' ')))));
  if (exact) return exact;
  const matched = docs.filter((doc) => {
    const template = DOCGEN_LIBRARY_TEMPLATES[doc.slug];
    return !!(template?.match && template.match(file, doc.serviceFormType));
  });
  if (matched.length === 1) return matched[0];
  const inferred = docgenDraftInferredType(file);
  const inferredDoc = inferred === 'blueCardApplication'
    ? docs.find((doc) => doc.slug === 'application' && doc.serviceFormType === 'blue_card')
    : inferred === 'employeeCardApplication'
      ? docs.find((doc) => doc.slug === 'application' && doc.serviceFormType === 'employee_card')
      : docs.find((doc) => ({
        accommodation: 'accommodation',
        workContract: 'work-contract',
        salary: 'salary',
        poa: 'poa',
      })[inferred] === doc.slug);
  if (inferredDoc) return inferredDoc;
  return allowSingleFallback && docs.length === 1 ? docs[0] : null;
}

function docgenDraftInferredType(file) {
  const text = docgenDraftMetadata(file).toLowerCase();
  if (/blue[\s_-]*card|modr[áa][\s_-]*karta/.test(text)) return 'blueCardApplication';
  if (/employee[\s_-]*card|zam[eě]stnaneck[áa][\s_-]*karta/.test(text)) return 'employeeCardApplication';
  if (/employee[\s_-]*onboarding|onboarding/.test(text)) return 'employeeOnboarding';
  if (/accommodation|ubytov[aá]n[ií]/.test(text)) return 'accommodation';
  if (/employment[\s_-]*contract|work[\s_-]*contract|pracovn[ií][\s_-]*smlouva/.test(text)) return 'workContract';
  if (/salary|wage|mzd/.test(text)) return 'salary';
  if (/power[\s_-]*of[\s_-]*attorney|pln[aá][\s_-]*moc|(?:^|[\s_-])poa(?:[\s_.-]|$)/.test(text)) return 'poa';
  return '';
}

function docgenDraftTypeLabel(file, doc, row, t) {
  if (doc?.slug === 'application') {
    if (row?.detail?.title) return row.detail.title;
    if (doc.serviceFormType === 'blue_card') return t('docgen.draftDocs.type.blueCardApplication');
    if (doc.serviceFormType === 'employee_card') return t('docgen.draftDocs.type.employeeCardApplication');
  }
  if (doc?.labelKey) return t(doc.labelKey);
  const inferred = docgenDraftInferredType(file);
  const inferredLabelKeys = {
    blueCardApplication: 'docgen.draftDocs.type.blueCardApplication',
    employeeCardApplication: 'docgen.draftDocs.type.employeeCardApplication',
    employeeOnboarding: 'docgen.draftDocs.type.employeeOnboarding',
    accommodation: 'docgen.doc.accommodation',
    workContract: 'docgen.doc.workContract',
    salary: 'docgen.doc.salary',
    poa: 'docgen.doc.poa',
  };
  if (inferred && inferredLabelKeys[inferred]) return t(inferredLabelKeys[inferred]);
  const source = String(file?.templateName || file?.template_name || file?.template || docgenDraftFileName(file) || '')
    .replace(/\.[^.]+$/, '')
    .replace(/(?:^|[\s_-]+)(?:template|draft|taslak|n[aá]vrh)(?=$|[\s_-]+)/gi, ' ')
    .replace(/[_-]+/g, ' ')
    .replace(/\s+/g, ' ')
    .trim();
  return source || row?.detail?.title || t('docgen.draftDocs.type.document');
}

function docgenDraftPdfDownloadUrl(file) {
  const fileId = file?.pdfId || file?.pdf_id || file?.pdf?.id || docgenDraftFileId(file);
  const base = String((window.YEYE_CONFIG && window.YEYE_CONFIG.FILL_API_URL) || '').replace(/\/$/, '');
  return fileId && base ? `${base}/public/download/${encodeURIComponent(fileId)}` : '';
}

function docgenDraftDocxDownloadUrl(file) {
  const fileId = file?.docId || file?.doc_id || file?.documentId || file?.document_id || docgenDraftFileId(file);
  const base = String((window.YEYE_CONFIG && window.YEYE_CONFIG.FILL_API_URL) || '').replace(/\/$/, '');
  return fileId && base ? `${base}/public/download/${encodeURIComponent(fileId)}?format=docx` : '';
}

function docgenDraftFiles(payload) {
  if (Array.isArray(payload)) return payload.filter(Boolean);
  const data = payload?.data;
  const files = payload?.draftDocuments || payload?.draft_documents || payload?.drafts || payload?.files || payload?.documents ||
    (Array.isArray(data) ? data : data?.draftDocuments || data?.draft_documents || data?.drafts || data?.files || data?.documents) || [];
  return Array.isArray(files) ? files.filter(Boolean) : [];
}

function DocumentApprovalModal({ file, doc, row, contact, fieldMappings, busy, onClose, onApprove }) {
  const { t } = useT();
  const [reviewed, setReviewed] = React.useState(false);
  const previewUrl = docgenDraftFileUrl(file) || docgenDraftPdfDownloadUrl(file);
  const fileId = docgenDraftFileId(file);
  const mappedRows = doc && window.YEYE_FIELD_MAPPING
    ? window.YEYE_FIELD_MAPPING.forForm(fieldMappings || [], doc.fieldForm || doc.formType || row?.formType, contact || {})
    : [];
  const filledCount = mappedRows.filter((mappedRow) => [mappedRow?.value, mappedRow?.v].some(docgenHasValue)).length;
  const title = doc ? t(doc.labelKey) : docgenDraftTypeLabel(file, doc, row, t);
  return (
    <Modal
      wide
      icon="scan-eye"
      title={t('docApprove.reviewAndApprove')}
      sub={title}
      onClose={() => !busy && onClose()}
      footer={(
        <>
          <Btn variant="ghost" disabled={busy} onClick={onClose}>{t('c.cancel')}</Btn>
          <Btn variant="primary" icon="badge-check" disabled={!reviewed || busy} onClick={onApprove}>
            {busy && <span className="docgen-spinner docgen-button-spinner" aria-hidden="true" />}
            {t('docApprove.approve')}
          </Btn>
        </>
      )}
    >
      <div className="doc-approval-review">
        <div className="doc-approval-meta">
          <span><Icon name="file-text" size={15} />{docgenDraftFileName(file) || title}</span>
          <span><Icon name="list-checks" size={15} />{filledCount}/{mappedRows.length || filledCount}</span>
        </div>
        {previewUrl ? (
          <iframe className="doc-approval-frame" title={docgenDraftFileName(file) || title} src={docgenPreviewUrl(previewUrl, fileId, String((window.YEYE_CONFIG && window.YEYE_CONFIG.FILL_API_URL) || '').replace(/\/$/, ''))} />
        ) : (
          <div className="docgen-panel-empty"><Icon name="file-search" size={28} /><span>{t('doc.noPreview')}</span></div>
        )}
        <label className="doc-approval-checkbox">
          <input type="checkbox" checked={reviewed} onChange={(event) => setReviewed(event.target.checked)} />
          <span>{t('docApprove.iHaveReviewed')}</span>
        </label>
      </div>
    </Modal>
  );
}

function DocGenGroupedDrafts({ contactId, contactEmail, serviceRows, refreshToken, onSelectDraft, selectedDraftKey }) {
  const { t } = useT();
  const [drafts, setDrafts] = React.useState(() => DOCGEN_DRAFT_CACHE.get(String(contactId || '')) || []);
  const [loading, setLoading] = React.useState(false);
  const explainerStorageKey = React.useMemo(() => docgenDraftExplainerStorageKey(contactEmail), [contactEmail]);
  const [explainerDismissed, setExplainerDismissed] = React.useState(() => {
    try { return localStorage.getItem(docgenDraftExplainerStorageKey(contactEmail)) === '1'; } catch (_) { return false; }
  });
  const [explainerClosing, setExplainerClosing] = React.useState(false);
  React.useEffect(() => {
    try { setExplainerDismissed(localStorage.getItem(explainerStorageKey) === '1'); } catch (_) { setExplainerDismissed(false); }
    setExplainerClosing(false);
  }, [explainerStorageKey]);
  const dismissExplainer = () => {
    if (explainerClosing) return;
    setExplainerClosing(true);
    window.setTimeout(() => {
      try { localStorage.setItem(explainerStorageKey, '1'); } catch (_) { /* Storage can be unavailable in private mode. */ }
      setExplainerDismissed(true);
      setExplainerClosing(false);
    }, 180);
  };
  const explainer = !explainerDismissed ? (
    <div className={'docgen-draft-explainer' + (explainerClosing ? ' is-closing' : '')}>
      <Icon name="info" size={17} />
      <span>{t('docApprove.draftExplainer')}</span>
      <button type="button" className="docgen-draft-explainer-close" aria-label={t('c.close')} onClick={dismissExplainer}><Icon name="x" size={15} /></button>
    </div>
  ) : null;
  React.useEffect(() => {
    let cancelled = false;
    const cacheKey = String(contactId || '');
    if (!contactId) { setDrafts([]); return () => { cancelled = true; }; }
    const hasCached = DOCGEN_DRAFT_CACHE.has(cacheKey);
    if (!hasCached) setLoading(true);
    const load = async () => {
      const cfg = window.YEYE_CONFIG || {};
      const base = String(cfg.FILL_API_URL || '').replace(/\/$/, '');
      if (!base) return;
      if (!hasCached) setLoading(true);
      try {
        const response = await fetch(`${base}/public/contacts/${encodeURIComponent(contactId)}/draft-documents?bucket=expat`, {
          headers: { 'Accept': 'application/json', ...(cfg.FILL_API_KEY ? { 'X-API-Key': cfg.FILL_API_KEY } : {}) },
        });
        if (!response.ok) return;
        const files = docgenDraftFiles(await response.json().catch(() => []));
        if (cancelled) return;
        DOCGEN_DRAFT_CACHE.set(cacheKey, files);
        setDrafts(files);
      } finally {
        if (!cancelled) setLoading(false);
      }
    };
    load().catch(() => {});
    return () => { cancelled = true; };
  }, [contactId, refreshToken]);
  const groups = React.useMemo(() => {
    const rows = serviceRows || [];
    const out = [];
    const byType = new Map();
    drafts.forEach((file, index) => {
      let row = null;
      let doc = null;
      const inferred = docgenDraftInferredType(file);
      const preferredSlug = ({ accommodation: 'accommodation', workContract: 'work-contract', salary: 'salary', poa: 'poa' })[inferred];
      const preferredRows = rows.filter((candidateRow) => (candidateRow.documents || []).some((candidateDoc) => (
        (inferred === 'blueCardApplication' && candidateDoc.slug === 'application' && candidateDoc.serviceFormType === 'blue_card') ||
        (inferred === 'employeeCardApplication' && candidateDoc.slug === 'application' && candidateDoc.serviceFormType === 'employee_card') ||
        (preferredSlug && candidateDoc.slug === preferredSlug)
      )));
      const candidateRows = preferredRows.concat(rows.filter((candidateRow) => !preferredRows.includes(candidateRow)));
      candidateRows.some((candidateRow) => {
        const candidateDoc = docgenDraftDocument(file, candidateRow.documents || [], false);
        if (!candidateDoc) return false;
        row = candidateRow;
        doc = candidateDoc;
        return true;
      });
      if (!doc && rows.length) {
        const appProps = file?.appProperties || file?.app_properties || {};
        const fallbackDocumentKeys = {
          blueCardApplication: 'application_form', employeeCardApplication: 'application_form',
          accommodation: 'accommodation_confirmation', workContract: 'work_contract',
          salary: 'salary_certificate', poa: 'power_of_attorney',
        };
        const explicitDocKey = file?.documentKey || file?.document_key || file?.docKey || file?.doc_key ||
          appProps.yeye_document_key || fallbackDocumentKeys[inferred] || '';
        if (explicitDocKey) {
          row = preferredRows[0] || rows[0];
          const baseDoc = row?.documents?.[0] || {};
          doc = {
            ...baseDoc,
            id: `${row?.serviceId || 'service'}-draft-${normalizeDocgenFieldKey(explicitDocKey)}`,
            slug: inferred || 'document',
            labelKey: ({
              blueCardApplication: 'docgen.draftDocs.type.blueCardApplication',
              employeeCardApplication: 'docgen.draftDocs.type.employeeCardApplication',
              accommodation: 'docgen.doc.accommodation', workContract: 'docgen.doc.workContract',
              salary: 'docgen.doc.salary', poa: 'docgen.doc.poa',
            })[inferred] || 'docgen.draftDocs.type.document',
            documentKey: explicitDocKey,
            serviceFormType: ({
              blueCardApplication: 'blue_card', employeeCardApplication: 'employee_card',
              employeeOnboarding: 'employee_onboarding', accommodation: 'accommodation_confirmation',
              workContract: 'work_contract', salary: 'salary_certificate', poa: 'power_of_attorney_salih',
            })[inferred] || baseDoc.serviceFormType || row?.formType || '',
          };
        }
      }
      const key = doc
        ? `${row?.serviceId || 'service'}:${doc.id || doc.documentKey || doc.slug}`
        : `inferred:${inferred || docgenDraftMetadata(file) || index}`;
      if (!byType.has(key)) {
        const group = { row, doc, sample: file, drafts: [] };
        byType.set(key, group);
        out.push(group);
      }
      byType.get(key).drafts.push(file);
    });
    return out;
  }, [drafts, serviceRows]);
  if (loading && drafts.length === 0) return (
    <div className="card-bd docgen-drafts-grouped">
      {explainer}
      <div className="docgen-panel-empty"><span className="docgen-spinner" /><span>{t('docgen.draftDocs.loading')}</span></div>
    </div>
  );
  if (groups.length === 0) return (
    <div className="card-bd docgen-drafts-grouped">
      {explainer}
      <div className="docgen-panel-empty"><Icon name="folder-open" size={28} /><span>{t('docgen.draftDocs.empty')}</span></div>
    </div>
  );
  return (
    <div className="card-bd docgen-drafts-grouped">
      {explainer}
      {groups.map((group, gi) => {
        const groupTitle = `${docgenDraftTypeLabel(group.sample, group.doc, group.row, t)} ${t('docgen.draftDocs.groupSuffix')}`.trim();
        return (
          <div className="docgen-drafts-group" key={(group.row?.serviceId || 'draft-type') + '-' + (group.doc?.id || docgenDraftInferredType(group.sample) || gi)}>
            <div className="docgen-drafts-group-title">{groupTitle}</div>
            <div className="docgen-drafts-group-list">
              {group.drafts.map((file, i) => {
                const fileKey = String(docgenDraftFileId(file) || docgenDraftFileName(file) || i);
                const fileName = docgenDraftFileName(file) || fileKey;
                const isActive = selectedDraftKey === fileKey;
                return (
                  <button
                    type="button"
                    key={fileKey}
                    className={'docgen-draft-item' + (isActive ? ' is-active' : '')}
                    onClick={() => onSelectDraft && onSelectDraft(file, fileKey, group.row, group.doc)}
                  >
                    <Icon name="file-text" size={14} />
                    <span className="grow" style={{ minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{fileName}</span>
                    <Icon name="chevron-right" size={13} />
                  </button>
                );
              })}
            </div>
          </div>
        );
      })}
    </div>
  );
}

function DocGeneratorPage({ purchasedServices, contact, fieldMappings, onRefresh, onOptimisticUpdate }) {
  const { t, lang, currency } = useT();
  const ACCOM_DIY_KEYS = new Set(['accom12_diy', 'accom24_diy', 'accom36_diy']);
  const diyServices = (purchasedServices || []).filter((p) => {
    if (!isDiyPurchasedService(p)) return false;
    const k = String((p && (p.k || p.key || p.serviceKey || p.productKey)) || '').toLowerCase();
    return !ACCOM_DIY_KEYS.has(k);
  });
  const [expandedServices, setExpandedServices] = React.useState(() => ({}));
  const [selectedDocId, setSelectedDocId] = React.useState('');
  const [results, setResults] = React.useState({});
  const [phase, setPhase] = React.useState('idle');
  const [error, setError] = React.useState('');
  const [statusIndex, setStatusIndex] = React.useState(0);
  const [generationCounts, setGenerationCounts] = React.useState({});
  const [fieldDrafts, setFieldDrafts] = React.useState({});
  const [savedFieldValues, setSavedFieldValues] = React.useState({});
  const [fieldModalOpen, setFieldModalOpen] = React.useState(false);
  const [savingFields, setSavingFields] = React.useState(false);
  const [confirmedOverrides, setConfirmedOverrides] = React.useState({});
  const [draftRefreshToken, setDraftRefreshToken] = React.useState(0);
  const [selectedDraftPreview, setSelectedDraftPreview] = React.useState(null);
  const [selectedDraftRow, setSelectedDraftRow] = React.useState(null);
  const [approvalDraft, setApprovalDraft] = React.useState(null);
  const [unapproveDraft, setUnapproveDraft] = React.useState(null);
  const [promotionBusy, setPromotionBusy] = React.useState(false);
  const requestRef = React.useRef(0);
  const contactId = contact && (contact.contactId || contact.id);
  const docTemplates = ['application', 'work-contract', 'salary', 'poa', 'accommodation'];
  // Accommodation is a standalone paid service. When purchased separately (accom12/24/36),
  // it gets its own docgen service card and is removed from the card services' document list
  // so the accommodation confirmation never shows twice.
  // Document generation for accommodation is DIY-only; professional accommodation is handled by YeYe.
  const accommodationPurchase = (purchasedServices || []).find((p) => ['accom12_diy', 'accom24_diy', 'accom36_diy'].includes(String((p && (p.k || p.key || p.serviceKey || p.productKey)) || '').toLowerCase()));
  const hasAccommodation = !!accommodationPurchase;
  const cardDocTemplates = hasAccommodation ? docTemplates.filter((slug) => slug !== 'accommodation') : docTemplates;
  const serviceRows = diyServices.map((purchase, index) => {
    const service = catalogServiceForPurchase(purchase.k) || purchase;
    const detail = purchasedServiceDetail(service, purchase, t, lang, currency);
    const rawKey = String(purchase.k || purchase.key || purchase.serviceKey || purchase.productKey || service.k || '').toLowerCase();
    const normalizedKey = typeof window.YEYE_NORMALIZE_SERVICE_KEY === 'function' ? window.YEYE_NORMALIZE_SERVICE_KEY(rawKey) : rawKey.replace(/_diy$/, '');
    const declaredFormType = String(purchase.formType || purchase.form_type || service.formType || service.form_type || '').toLowerCase();
    let formType = docgenCardFormType(rawKey, normalizedKey, declaredFormType);
    if (!formType && (['employee_onboarding', 'employee_onboarding_diy'].includes(rawKey) || normalizedKey === 'employee_onboarding')) formType = 'employee_onboarding';
    else if (['ce', 'company_establishment', 'sro_establishment', 'sro_establishment_diy'].includes(rawKey) || ['ce', 'company_establishment', 'sro_establishment'].includes(normalizedKey)) formType = 'ce';
    else if (declaredFormType === 'company_establishment') formType = 'ce';
    else if (['employee_onboarding', 'ce'].includes(declaredFormType)) formType = declaredFormType;
    const isCardService = formType === 'blue_card' || formType === 'employee_card';
    const serviceId = String(purchase.id || rawKey || 'service') + '-' + index;
    const documents = (isCardService ? cardDocTemplates : ['application']).map((slug) => {
      const template = DOCGEN_LIBRARY_TEMPLATES[slug] || DOCGEN_LIBRARY_TEMPLATES.application;
      const fillFormType = typeof template.fillFormType === 'function' ? template.fillFormType(formType) : formType;
      const fieldForm = typeof template.fieldForm === 'function' ? template.fieldForm(formType) : formType;
      const templateId = typeof template.templateId === 'function' ? template.templateId(formType) : template.templateId;
      return ({
      ...template,
      slug,
      id: serviceId + '-' + slug,
      serviceFormType: template.processType || formType,
      formType: fillFormType,
      fieldForm,
      documentKey: template.documentKey,
      templateId,
      disabled: !formType,
    });
    });
    return { purchase, service, detail, serviceId, documents };
  });
  if (hasAccommodation) {
    const accService = catalogServiceForPurchase('accommodation') || accommodationPurchase;
    const accFormType = 'employee_onboarding';
    const accServiceId = 'accommodation-service';
    const accDocuments = ['accommodation'].map((slug) => {
      const template = DOCGEN_LIBRARY_TEMPLATES[slug] || DOCGEN_LIBRARY_TEMPLATES.application;
      const fillFormType = typeof template.fillFormType === 'function' ? template.fillFormType(accFormType) : accFormType;
      const fieldForm = typeof template.fieldForm === 'function' ? template.fieldForm(accFormType) : accFormType;
      return { ...template, slug, id: accServiceId + '-' + slug, serviceFormType: template.processType || accFormType, formType: fillFormType, fieldForm, documentKey: template.documentKey, templateId: template.templateId, disabled: !fillFormType };
    });
    const accKey = String((accommodationPurchase && (accommodationPurchase.k || accommodationPurchase.key || accommodationPurchase.serviceKey || accommodationPurchase.productKey)) || '').toLowerCase();
    const accMonths = accKey.includes('24') ? '24' : accKey.includes('36') ? '36' : '12';
    const accDetail = { ...(purchasedServiceDetail(accService, accommodationPurchase, t, lang, currency) || {}), title: t('docgen.accommodationServiceTitle').replace('{months}', accMonths) };
    serviceRows.push({ purchase: accommodationPurchase, service: accService, detail: accDetail, serviceId: accServiceId, documents: accDocuments });
  }
  const allDocuments = serviceRows.reduce((items, row) => items.concat(row.documents), []);
  const selectedDoc = allDocuments.find((doc) => doc.id === selectedDocId) || null;
  const selectedResult = selectedDoc ? results[selectedDoc.id] : null;
  const rawMappedRows = selectedDoc && window.YEYE_FIELD_MAPPING
    ? window.YEYE_FIELD_MAPPING.forForm(fieldMappings || [], selectedDoc.fieldForm || selectedDoc.formType, contact || {})
    : [];
  const mappedRows = rawMappedRows.map((row) => {
    const savedKey = docgenRowIdentifiers(row).find((key) => Object.prototype.hasOwnProperty.call(savedFieldValues, key));
    return savedKey ? { ...row, value: savedFieldValues[savedKey], v: savedFieldValues[savedKey] } : row;
  });
  const requiredFieldSlug = selectedDoc?.slug === 'application' ? selectedDoc.formType : selectedDoc?.slug;
  const selectedRows = filterDocgenRequiredRows(mappedRows, requiredFieldSlug);
  const selectedRowGroups = groupDocgenRows(selectedRows, fieldMappings || [], t);

  const countKey = React.useCallback((doc) => `yeye-docgen-count-${contactId || 'unknown'}-${doc.formType}-${doc.slug}`, [contactId]);
  const readCount = React.useCallback((doc) => {
    try { return Math.max(0, parseInt(localStorage.getItem(countKey(doc)) || '0', 10) || 0); } catch (_) { return 0; }
  }, [countKey]);
  const selectedCount = selectedDoc ? (generationCounts[selectedDoc.id] ?? readCount(selectedDoc)) : 0;

  React.useEffect(() => {
    if (phase !== 'running') return undefined;
    setStatusIndex(0);
    const timer = window.setInterval(() => setStatusIndex((value) => (value + 1) % 3), 3500);
    return () => window.clearInterval(timer);
  }, [phase]);
  React.useEffect(() => () => { requestRef.current += 1; }, []);
  React.useEffect(() => { setSavedFieldValues({}); setConfirmedOverrides({}); }, [contactId]);

  const generateDocument = React.useCallback(async (activeContact, doc) => {
    const cfg = window.YEYE_CONFIG || {};
    const base = String(cfg.FILL_API_URL || '').replace(/\/$/, '');
    if (!base) throw new Error('backend-not-configured');
    const activeContactId = activeContact && (activeContact.contactId || activeContact.id);
    if (!activeContactId) throw new Error('missing-contact');
    const url = new URL(base + '/public/fill');
    url.searchParams.set('form_type', doc.formType);
    if (doc.documentKey) url.searchParams.set('document_key', doc.documentKey);
    if (docgenExplicitTemplateId(doc.templateId)) url.searchParams.set('template_id', doc.templateId);
    url.searchParams.set('create_pdf', 'true');
    url.searchParams.set('sync_sheet', 'false');
    url.searchParams.set('target', 'draft');
    url.searchParams.set('bucket', 'expat');
    const contactName = [activeContact.firstName, activeContact.lastName].filter(Boolean).join(' ') || activeContact.name || activeContact.email || '';
    const response = await fetch(url.toString(), {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', ...(cfg.FILL_API_KEY ? { 'X-API-Key': cfg.FILL_API_KEY } : {}) },
      body: JSON.stringify({
        contactId: activeContactId,
        contactName,
        formType: doc.formType,
        documentKey: doc.documentKey,
        templateIds: docgenExplicitTemplateId(doc.templateId) ? [doc.templateId] : undefined,
        contactFields: {},
        target: 'draft',
      }),
    });
    if (!response.ok) {
      const responseText = await response.text().catch(() => '');
      throw new Error('http-' + response.status + (responseText ? ': ' + responseText.slice(0, 120) : ''));
    }
    return normalizeDocgenResult(await response.json().catch(() => ({})), doc, base);
  }, []);

  const runGeneration = React.useCallback(async () => {
    if (!selectedDoc || selectedDoc.disabled || phase === 'running') return;
    const request = requestRef.current + 1;
    requestRef.current = request;
    setPhase('running');
    setError('');
    try {
      const payload = await generateDocument(contact, selectedDoc);
      if (requestRef.current !== request) return;
      const nextCount = readCount(selectedDoc) + 1;
      try { localStorage.setItem(countKey(selectedDoc), String(nextCount)); } catch (_) { /* Storage can be unavailable in private mode. */ }
      setGenerationCounts((previous) => ({ ...previous, [selectedDoc.id]: nextCount }));
      setResults((previous) => ({ ...previous, [selectedDoc.id]: payload }));
      setPhase('success');
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('docgen.success'));
    } catch (generationError) {
      if (requestRef.current !== request) return;
      console.warn('DocGen failed', generationError);
      setError(t('docgen.errorGeneric'));
      setPhase('error');
    }
  }, [contact, countKey, generateDocument, phase, readCount, selectedDoc, t]);

  const startDocumentGeneration = () => {
    if (selectedRows.length) {
      setFieldModalOpen(true);
      return;
    }
    if (window.YEYE_TOAST) window.YEYE_TOAST(t('docgen.allRequiredReady'));
    runGeneration();
  };

  const selectDocument = (doc) => {
    if (doc.disabled) return;
    requestRef.current += 1;
    setSelectedDocId(doc.id);
    setPhase(results[doc.id] ? 'success' : 'idle');
    setError('');
    setFieldDrafts({});
    setFieldModalOpen(false);
  };
  const refreshDraftLifecycle = React.useCallback(async () => {
    DOCGEN_DRAFT_CACHE.delete(String(contactId || ''));
    setDraftRefreshToken((value) => value + 1);
    setSelectedDraftPreview(null);
    setApprovalDraft(null);
    setUnapproveDraft(null);
    if (onRefresh) await Promise.resolve(onRefresh());
  }, [contactId, onRefresh]);
  const promoteDraft = React.useCallback(async (entry) => {
    if (!entry || promotionBusy) return;
    const { file, doc, row } = entry;
    const fileId = docgenDraftFileId(file);
    if (!fileId || !doc || !row || !contactId) return;
    const cfg = window.YEYE_CONFIG || {};
    const base = String(cfg.FILL_API_URL || '').replace(/\/$/, '');
    if (!base) return;
    const processType = doc.serviceFormType || row.formType || '';
    if (!processType && window.YEYE_CONFIG?.DEV) console.warn('Draft promotion has no processType.', { docKey: doc.documentKey || doc.slug });
    setPromotionBusy(true);
    try {
      const response = await fetch(`${base}/public/contacts/${encodeURIComponent(contactId)}/documents/${encodeURIComponent(fileId)}/promote?target_sub=Relocation&bucket=expat`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', ...(cfg.FILL_API_KEY ? { 'X-API-Key': cfg.FILL_API_KEY } : {}) },
        body: JSON.stringify({ docKey: doc.documentKey || doc.slug, processType }),
      });
      if (!response.ok) throw new Error('http-' + response.status);
      setConfirmedOverrides((previous) => ({ ...previous, [doc.id]: true }));
      window.dispatchEvent(new CustomEvent('yeye:documents-changed', { detail: { contactId, action: 'approved' } }));
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('docgen.draftDocs.confirmSuccess'));
      await refreshDraftLifecycle();
    } catch (error) {
      console.warn('Draft confirm failed', error);
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('docgen.draftDocs.errorGeneric'));
    } finally {
      setPromotionBusy(false);
    }
  }, [contactId, promotionBusy, refreshDraftLifecycle, t]);
  const unpromoteDraft = React.useCallback(async (entry) => {
    if (!entry || promotionBusy || !contactId) return;
    const promotion = docgenDraftPromotion(entry.file);
    if (!promotion.docId) return;
    const cfg = window.YEYE_CONFIG || {};
    const base = String(cfg.FILL_API_URL || '').replace(/\/$/, '');
    if (!base) return;
    const processType = entry.doc?.serviceFormType || entry.row?.formType || '';
    if (!processType && window.YEYE_CONFIG?.DEV) console.warn('Draft unapproval has no processType.', { docKey: promotion.docKey || entry.doc?.documentKey || '' });
    setPromotionBusy(true);
    try {
      const response = await fetch(`${base}/public/contacts/${encodeURIComponent(contactId)}/documents/${encodeURIComponent(promotion.docId)}/unpromote`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', ...(cfg.FILL_API_KEY ? { 'X-API-Key': cfg.FILL_API_KEY } : {}) },
        body: JSON.stringify({ docKey: promotion.docKey || entry.doc?.documentKey || '', processType }),
      });
      if (!response.ok) throw new Error('http-' + response.status);
      if (entry.doc) setConfirmedOverrides((previous) => ({ ...previous, [entry.doc.id]: false }));
      window.dispatchEvent(new CustomEvent('yeye:documents-changed', { detail: { contactId, action: 'unapproved' } }));
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('docgen.draftDocs.undoSuccess'));
      await refreshDraftLifecycle();
    } catch (error) {
      console.warn('Draft unapprove failed', error);
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('docgen.draftDocs.errorGeneric'));
    } finally {
      setPromotionBusy(false);
    }
  }, [contactId, promotionBusy, refreshDraftLifecycle, t]);
  const translatedStatus = (key, fallback) => {
    const value = t(key);
    return value === key ? fallback : value;
  };
  const statusMessages = [
    translatedStatus('docgen.rotating.1', t('docgen.generating')),
    translatedStatus('docgen.rotating.2', 'Fetching your data'),
    translatedStatus('docgen.rotating.3', 'Almost done'),
  ];
  const previewUrl = selectedResult && selectedResult.previewUrl;
  const saveMissingFields = async () => {
    if (!selectedDoc || !selectedRows.length || savingFields) return;
    setSavingFields(true);
    try {
      const patch = await saveApplicationFormAnswers(contact, fieldDrafts, selectedRows, selectedDoc.fieldForm || selectedDoc.formType);
      const savedValues = {};
      selectedRows.forEach((row) => {
        const draftKey = [row.updateKey, row.fieldName, row.key, row.formKey]
          .find((key) => key && Object.prototype.hasOwnProperty.call(fieldDrafts, key));
        if (!draftKey) return;
        docgenRowIdentifiers(row).forEach((identifier) => { savedValues[identifier] = fieldDrafts[draftKey]; });
      });
      setSavedFieldValues((previous) => ({ ...previous, ...savedValues }));
      if (onOptimisticUpdate) onOptimisticUpdate(contactId, patch);
      const remainingRows = selectedRows.filter((row) => {
        const draftKey = [row.updateKey, row.fieldName, row.key, row.formKey]
          .find((key) => key && Object.prototype.hasOwnProperty.call(fieldDrafts, key));
        return !draftKey || !docgenHasValue(fieldDrafts[draftKey]);
      });
      setFieldDrafts({});
      setFieldModalOpen(false);
      if (onRefresh) await Promise.resolve(onRefresh());
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('form.savedGhl') || 'Saved');
      if (!remainingRows.length) {
        if (window.YEYE_TOAST) window.YEYE_TOAST(t('docgen.allRequiredReady'));
        await runGeneration();
      }
    } catch (err) {
      console.warn('DocGen field save failed', err);
      if (window.YEYE_TOAST) window.YEYE_TOAST((err && err.message) || 'Save failed');
    } finally {
      setSavingFields(false);
    }
  };
  return (
    <>
      <PageHead title={t('docgen.pageTitle')} sub={t('docgen.pageIntro')} />
      <section aria-label={t('docgen.pageTitle')}>
        {serviceRows.length === 0 ? (
          <Card>
            <div className="card-bd" style={{ padding: 24, textAlign: 'center' }}>
              <div style={{ color: 'var(--ink-500)', fontSize: 13.5 }}>{t('docgen.emptyState')}</div>
            </div>
          </Card>
        ) : (
          <div className="docgen-wizard">
            <aside className="docgen-services-panel">
              <div className="flex ac gap-8 mb-12">
                <div className="eyebrow">{t('docgen.leftTitle')}</div>
                <Badge tone="info">{serviceRows.length}</Badge>
              </div>
              <div className="docgen-service-list">
                {serviceRows.map((row, index) => {
                  const isExpanded = expandedServices[row.serviceId] ?? index === 0;
                  return (
                    <Card className="docgen-service-card" key={row.serviceId}>
                      <button type="button" className="docgen-service-toggle" aria-expanded={isExpanded} onClick={() => setExpandedServices((previous) => ({ ...previous, [row.serviceId]: !isExpanded }))}>
                        <span className="docgen-service-icon"><Icon name={P.catIcon[row.service.cat] || 'package-check'} size={17} /></span>
                        <span>{row.detail.title}{row.detail.mode ? ' · ' + row.detail.mode : ''}</span>
                        <Icon name={isExpanded ? 'chevron-up' : 'chevron-down'} size={15} />
                      </button>
                      {isExpanded && (
                        <>
                          <div className="docgen-document-list">
                            {row.documents.map((doc) => {
                              const fieldKey = docgenConfirmedFieldKey(doc);
                              const confirmed = Object.prototype.hasOwnProperty.call(confirmedOverrides, doc.id)
                                ? confirmedOverrides[doc.id]
                                : !!(fieldKey && contactFieldValue(contact, [fieldKey]));
                              return (
                                <button type="button" key={doc.id} disabled={doc.disabled} className={'docgen-document-row' + (selectedDocId === doc.id ? ' is-selected' : '') + (doc.disabled ? ' is-disabled' : '')} onClick={() => selectDocument(doc)}>
                                  <Icon name={doc.icon} size={15} />
                                  <span className="docgen-document-label">{t(doc.labelKey)}{confirmed && <Badge tone="ok">{t('docgen.draftDocs.confirmed')} ✓</Badge>}</span>
                                  {doc.disabled && <small>{t('docgen.templateComingSoon')}</small>}
                                </button>
                              );
                            })}
                            <button
                              type="button"
                              className={'docgen-document-row' + (!selectedDocId && selectedDraftRow?.serviceId === row.serviceId ? ' is-selected' : '')}
                              onClick={() => { setSelectedDocId(''); setSelectedDraftRow(row); setSelectedDraftPreview(null); }}
                            >
                              <Icon name="folder-open" size={15} />
                              <span className="docgen-document-label">{t('docgen.draftDocs.title')}</span>
                            </button>
                          </div>
                        </>
                      )}
                    </Card>
                  );
                })}
              </div>
            </aside>

            <main className="docgen-detail-panel">
              <Card style={{ height: '100%' }}>
                {!selectedDoc ? (
                  <DocGenGroupedDrafts
                    contactId={contactId}
                    contactEmail={contact?.email}
                    serviceRows={selectedDraftRow ? [selectedDraftRow] : serviceRows}
                    refreshToken={draftRefreshToken}
                    selectedDraftKey={selectedDraftPreview?.key || ''}
                    onSelectDraft={(file, key, row, doc) => { setSelectedDraftPreview({ key, file, name: docgenDraftFileName(file), url: docgenDraftFileUrl(file) || docgenDraftPdfDownloadUrl(file), row: row || selectedDraftRow, doc }); }}
                  />
                ) : (
                  <div className="card-bd docgen-detail-body">
                    <div className="docgen-detail-heading">
                      <span className="docgen-detail-icon"><Icon name={selectedDoc.icon} size={22} /></span>
                      <div><div className="h2">{t(selectedDoc.labelKey)}</div><div className="muted">{t(selectedDoc.descKey)}</div></div>
                    </div>
                    <div className="docgen-count"><Icon name="history" size={14} />{t('docgen.generatedNTimes').replace('{n}', selectedCount)}</div>
                    <div className="docgen-field-summary">
                      <div className="docgen-missing-head">
                        <div>
                          <div className="strong">{t('docgen.fieldsTitle')}</div>
                          <div className="muted" style={{ fontSize: 12 }}>{t('docgen.fieldsSub')}</div>
                        </div>
                        {selectedRows.length > 0 && <Badge tone="info">{selectedRows.length}</Badge>}
                      </div>
                      {selectedRows.length > 0 ? (
                        <Btn variant="ghost" icon="form-input" onClick={() => setFieldModalOpen(true)}>
                          {t('docgen.openFieldsBtn')}
                        </Btn>
                      ) : (
                        <div className="muted" style={{ fontSize: 12 }}>{t('docgen.allRequiredReady')}</div>
                      )}
                    </div>
                    <div style={{ marginTop: 'auto' }}>
                      <Btn className="btn-block" variant="primary" size="lg" icon="file-plus-2" disabled={phase === 'running'} onClick={startDocumentGeneration}>
                        {phase === 'running' ? t('docgen.generating') : t('docgen.generateBtn')}
                      </Btn>
                      <div className="muted" style={{ fontSize: 12, marginTop: 10 }}>{t('docgen.readyToGenerate')}</div>
                    </div>
                  </div>
                )}
              </Card>
            </main>

            <aside className="docgen-preview-panel">
              <Card>
                <CardHead icon="scan-eye" title={t('docgen.previewTitle')} />
                <div className="card-bd docgen-preview-body">
                  {(!selectedDoc && selectedDraftPreview) ? (
                    selectedDraftPreview.url ? (
                      <>
                        <iframe className="docgen-preview-frame" title={selectedDraftPreview.name || 'Draft'} src={docgenPreviewUrl(selectedDraftPreview.url, docgenDraftFileId(selectedDraftPreview.file), String((window.YEYE_CONFIG && window.YEYE_CONFIG.FILL_API_URL) || '').replace(/\/$/, ''))} />
                        {(() => {
                          const initialRow = selectedDraftPreview.row || selectedDraftRow;
                          const promotion = docgenDraftPromotion(selectedDraftPreview.file);
                          const candidateDocs = initialRow?.documents || allDocuments;
                          const confirmDoc = selectedDraftPreview.doc || docgenDraftDocument(selectedDraftPreview.file, candidateDocs);
                          const previewRow = initialRow || serviceRows.find((candidateRow) => (candidateRow.documents || []).some((doc) => doc.id === confirmDoc?.id));
                          const pdfDownloadUrl = docgenDraftPdfDownloadUrl(selectedDraftPreview.file);
                          const docxDownloadUrl = docgenDraftDocxDownloadUrl(selectedDraftPreview.file);
                          return (
                            <>
                              <div className="docgen-preview-actions">
                                {pdfDownloadUrl && <a className="btn btn-primary" href={pdfDownloadUrl} download target="_blank" rel="noopener noreferrer"><Icon name="download" size={16} />{t('doc.download')}</a>}
                                {docxDownloadUrl && <a className="btn btn-ghost" href={docxDownloadUrl} download target="_blank" rel="noopener noreferrer"><Icon name="file-text" size={16} />{t('doc.downloadDocx')}</a>}
                                {!promotion.promoted && (
                                  <Btn variant="primary" icon="scan-eye" disabled={!confirmDoc || !previewRow || promotionBusy} onClick={() => setApprovalDraft({ file: selectedDraftPreview.file, doc: confirmDoc, row: previewRow })}>
                                    {t('docApprove.reviewAndApprove')}
                                  </Btn>
                                )}
                                {promotion.promoted && (
                                  <>
                                    <Badge tone="ok">{t('docApprove.approved')} ✓{docgenApprovalDate(selectedDraftPreview.file) ? ` · ${docgenApprovalDate(selectedDraftPreview.file)}` : ''}</Badge>
                                    <Btn variant="ghost" size="sm" icon="undo-2" disabled={!promotion.docId || promotionBusy} onClick={() => setUnapproveDraft({ file: selectedDraftPreview.file, doc: confirmDoc, row: previewRow })}>{t('docApprove.unapprove')}</Btn>
                                  </>
                                )}
                                <Btn variant="ghost" icon="x" disabled={promotionBusy} onClick={() => { setSelectedDraftPreview(null); }}>{t('c.close')}</Btn>
                              </div>
                            </>
                          );
                        })()}
                      </>
                    ) : (
                      <div className="docgen-panel-empty"><Icon name="file-search" size={28} /><span>{t('doc.noPreview')}</span></div>
                    )
                  ) : phase === 'running' ? (
                    <div className="docgen-preview-status" role="status" aria-live="polite"><span className="docgen-spinner docgen-loading-spinner" /><div>{statusMessages[statusIndex]}</div></div>
                  ) : phase === 'error' ? (
                    <div className="docgen-preview-status is-error"><Icon name="circle-x" size={30} /><div>{error}</div><Btn variant="primary" size="sm" icon="refresh-cw" onClick={runGeneration}>{t('docgen.regenerateBtn')}</Btn></div>
                  ) : selectedResult ? (
                    <>
                      {previewUrl ? (
                        <iframe className="docgen-preview-frame" title={t(selectedDoc.labelKey)} src={previewUrl} />
                      ) : (
                        <div className="docgen-panel-empty"><Icon name="file-search" size={28} /><span>{t('doc.noPreview')}</span></div>
                      )}
                      <div className="docgen-preview-actions">
                        {selectedResult.downloadUrl && <a className="btn btn-primary" href={selectedResult.downloadUrl} download={selectedResult.fileName || true} target="_blank" rel="noopener noreferrer"><Icon name="download" size={16} />{t('docgen.downloadPdf')}</a>}
                        <Btn variant="ghost" icon="refresh-cw" onClick={runGeneration}>{t('docgen.regenerateBtn')}</Btn>
                      </div>
                      <div className="docgen-fields-filled">{t('docgen.fieldsFilled').replace('{n}', selectedResult.placeholdersReplaced || 0)}</div>
                      {selectedDoc && selectedDoc.documentKey === 'accommodation_confirmation' && (
                        <div className="docgen-print-note"><Icon name="pen-line" size={14} />{t('docgen.accommodation.printNote')}</div>
                      )}
                    </>
                  ) : (
                    <div className="docgen-panel-empty"><Icon name="file-search" size={28} /><span>{t('docgen.previewEmpty')}</span></div>
                  )}
                </div>
              </Card>
            </aside>
          </div>
        )}
      </section>
      {fieldModalOpen && selectedDoc && (
        <Modal
          title={t('docgen.completeMissingTitle')}
          sub={t(selectedDoc.labelKey)}
          icon="form-input"
          onClose={() => !savingFields && setFieldModalOpen(false)}
          footer={(
            <>
              <Btn variant="ghost" disabled={savingFields} onClick={() => setFieldModalOpen(false)}>{t('c.cancel')}</Btn>
              <Btn variant="ghost" icon="wand" disabled={savingFields} onClick={() => {
                const missingCount = selectedRows.length;
                const confirmMsg = (t('docgen.generateAnywayConfirm') || 'You have {n} missing required field(s). Generate anyway?').replace('{n}', missingCount);
                if (window.confirm(confirmMsg)) {
                  setFieldModalOpen(false);
                  runGeneration();
                }
              }}>{t('docgen.generateAnyway') || 'Generate anyway'}</Btn>
              <Btn variant="primary" icon={savingFields ? 'loader-circle' : 'save'} disabled={savingFields || Object.keys(fieldDrafts).length === 0} onClick={saveMissingFields}>
                {savingFields ? t('c.saving') : t('c.save')}
              </Btn>
            </>
          )}
        >
          <div className="docgen-missing-sections">
            {selectedRowGroups.map((section) => (
              <section className="docgen-missing-section" key={section.id}>
                <div className="docgen-missing-section-head"><span>{section.title}</span></div>
                <div className="docgen-missing-grid">
                  {section.rows.map((row) => {
                    const key = row.updateKey || row.fieldName || row.key || row.formKey;
                    const kind = applicationFieldKind(row, 'text');
                    const options = applicationFieldOptions(row, null, lang);
                    const label = stripSectionContext(labelFromPlaceholder(row, lang), row.__docgenSectionId || section.id, section.title, lang);
                    return (
                      <Field key={key} label={label}>
                        {window.renderProfileFieldWidget({
                          kind,
                          value: fieldDrafts[key] ?? row.value ?? row.v ?? '',
                          onChange: (value) => setFieldDrafts((previous) => ({ ...previous, [key]: value })),
                          options,
                          label,
                          lang,
                          placeholder: t('form.missing'),
                        })}
                      </Field>
                    );
                  })}
                </div>
              </section>
            ))}
          </div>
        </Modal>
      )}
      {approvalDraft && (
        <DocumentApprovalModal
          file={approvalDraft.file}
          doc={approvalDraft.doc}
          row={approvalDraft.row}
          contact={contact}
          fieldMappings={fieldMappings}
          busy={promotionBusy}
          onClose={() => setApprovalDraft(null)}
          onApprove={() => promoteDraft(approvalDraft)}
        />
      )}
      {unapproveDraft && (
        <Modal
          icon="undo-2"
          title={t('docApprove.unapprove')}
          onClose={() => !promotionBusy && setUnapproveDraft(null)}
          footer={(
            <>
              <Btn variant="ghost" disabled={promotionBusy} onClick={() => setUnapproveDraft(null)}>{t('c.cancel')}</Btn>
              <Btn variant="primary" disabled={promotionBusy} onClick={() => unpromoteDraft(unapproveDraft)}>{t('c.confirm')}</Btn>
            </>
          )}
        >
          <div style={{ lineHeight: 1.6 }}>{t('docApprove.unapproveConfirm')}</div>
        </Modal>
      )}
    </>
  );
}

/* ===== DOCUMENTS ===== */
function Chip({ active, onClick, children }) {
  return <button type="button" onClick={onClick}
    style={{ border: '1px solid ' + (active ? 'var(--brand-300)' : 'var(--line)'), background: active ? 'var(--brand-50)' : 'var(--surface-2)', color: active ? 'var(--brand-700)' : 'var(--ink)', padding: '6px 12px', borderRadius: 999, fontSize: 12.5, fontWeight: 500 }}>{children}</button>;
}

const ARCHIVE_FOLDER_NAMES = {
  f_relocation: 'Relocation',
  f_accommodation_re: 'Accommodation & Real Estate',
  f_translations: 'Translations',
  f_osvc: 'Free Trade Licence (OSVC)',
  f_sro: 'LTD (SRO) Set-up',
  f_tax: 'Tax & Financial',
  f_vehicle: 'Vehicle & Driving Licence',
  f_legal: 'Legal Support',
  f_permanent: 'Permanent & Citizenship',
  f_education: 'Education',
  f_identity: 'Identity and Passaport',
  f_health: 'Health',
};
function folderTkToSubName(tk, folder) {
  if (folder && folder.archiveName) return folder.archiveName;
  return ARCHIVE_FOLDER_NAMES[tk] || String(tk || '').replace(/^f_/, '').replace(/\b\w/g, (s) => s.toUpperCase());
}
function subNameToFolderTk(name) {
  if (name === 'Blue Card' || name === 'Employee Card') return 'f_relocation';
  const found = Object.keys(ARCHIVE_FOLDER_NAMES).find((tk) => ARCHIVE_FOLDER_NAMES[tk] === name);
  return found || '';
}
function driveFileMeta(file) {
  const bits = [];
  if (file.modifiedTime) {
    const d = new Date(file.modifiedTime);
    bits.push(Number.isNaN(d.getTime()) ? String(file.modifiedTime).slice(0, 10) : d.toLocaleDateString(undefined, { day: 'numeric', month: 'short' }));
  }
  if (file.size) bits.push(Math.max(1, Math.round(Number(file.size) / 1024)) + ' KB');
  return bits.join(' · ');
}

function FolderArchiveCard({ folder, allItems, archiveSubfolders, openFolderTk, onToggle }) {
  const { t } = useT();
  const folderItems = allItems.filter((x) => x.folderK === folder.tk);
  const archiveInfo = archiveSubfolders[folderTkToSubName(folder.tk, folder)] || {};
  const directFileCount = Number(archiveInfo.fileCount || 0);
  const totalCount = Math.max(folderItems.length, directFileCount + folderItems.filter((x) => x.source !== 'drive').length);
  const doneCount = folderItems.filter((x) => x.ok).length;
  const pendingCount = Math.max(0, totalCount - doneCount);
  const active = openFolderTk === folder.tk;
  const cardStyle = active
    ? { padding: 0, overflow: 'hidden', borderColor: 'var(--brand-300)', background: 'var(--brand-50)' }
    : { padding: 0, overflow: 'hidden' };
  return (
    <Card style={cardStyle}>
      <div onClick={() => onToggle(folder.tk)} className="pointer flex ac gap-12" style={{ padding: '14px 16px' }}>
        <div className="hd-ic" style={{ width: 40, height: 40, background: active ? 'var(--brand-100)' : 'var(--surface-3)', color: active ? 'var(--brand-700)' : 'var(--ink-500)' }}><Icon name={folder.ic} size={19} /></div>
        <div className="grow">
          <div className="strong" style={{ fontSize: 14 }}>{folder.label || t(folder.tk)}</div>
          {totalCount > 0 && (
            <div className="dim" style={{ fontSize: 12 }}>{doneCount}/{totalCount} {t('col.done')}{pendingCount > 0 ? ' · ' + pendingCount + ' ' + t('flt.pending').toLowerCase() : ''}</div>
          )}
        </div>
        {totalCount > 0 && pendingCount > 0 && <Badge tone="warn" dot>{pendingCount}</Badge>}
        <Icon name={active ? 'chevron-down' : 'chevron-right'} size={16} className="dim" />
      </div>
    </Card>
  );
}

function driveFileId(url) {
  const raw = String(url || '');
  const pathMatch = raw.match(/\/d\/([^/?#]+)/);
  if (pathMatch) return pathMatch[1];
  try { return new URL(raw).searchParams.get('id') || ''; } catch (_) { return ''; }
}

function driveDownloadUrl(file) {
  const id = (file && file.id) || driveFileId(file && (file.webViewLink || file.url));
  const base = String((window.YEYE_CONFIG && window.YEYE_CONFIG.FILL_API_URL) || '').replace(/\/$/, '');
  return id && base ? `${base}/public/download/${encodeURIComponent(id)}` : '';
}

function drivePreviewUrl(url) {
  const raw = String(url || '');
  if (/drive\.google\.com\/file\/d\//.test(raw)) return raw.replace(/\/view(?:\?.*)?$/, '/preview');
  if (/docs\.google\.com\/\w+\/d\/[^/]+\/edit/i.test(raw)) return raw.replace(/\/edit(?:[?#].*)?$/i, '/preview');
  return raw;
}

function DocumentDetailModal({ file, onClose }) {
  const { t } = useT();
  if (!file) return null;
  const webViewLink = file.webViewLink || file.url || '';
  const downloadUrl = driveDownloadUrl(file);
  return (
    <Modal wide icon={file.ic || 'file-text'} title={file.label || file.name || t('doc.file')} sub={file.meta || t('doc.uploaded')} onClose={onClose}
      footer={<>
        <a className="btn btn-primary btn-lg" href={downloadUrl || webViewLink} download target="_blank" rel="noopener noreferrer"><Icon name="download" size={16} />{t('doc.download')}</a>
      </>}>
      {webViewLink ? (
        <iframe
          src={drivePreviewUrl(webViewLink)}
          title={file.label || file.name || t('doc.file')}
          style={{ display: 'block', width: '100%', height: 'min(58vh, 620px)', border: '1px solid var(--line)', borderRadius: 12, background: 'var(--surface-2)' }}
        />
      ) : (
        <div className="flex col ac jc gap-10" style={{ minHeight: 260, textAlign: 'center', color: 'var(--ink-500)' }}>
          <Icon name="file-question" size={32} />
          <div className="strong">{t('doc.noPreview')}</div>
        </div>
      )}
    </Modal>
  );
}

function FolderExpandedBody({ folder, allItems, filter, openModal, openUpload, onOpenDocument, onUnapprove, unapproveBusy }) {
  const { t } = useT();
  if (!folder) return null;
  const folderItems = allItems.filter((x) => x.folderK === folder.tk);
  const filteredItems = filter === 'all' ? folderItems : (filter === 'done' ? folderItems.filter((x) => x.ok) : folderItems.filter((x) => !x.ok));
  return (
    <div style={{ padding: '10px 12px' }}>
      {folder.subs.map((sub) => {
        const subItems = filteredItems.filter((x) => x.subK === sub.tk);
        const subDone = subItems.filter((x) => x.ok).length;
        return (
          <div key={sub.tk} style={{ padding: '10px 6px', borderTop: '1px solid var(--line-2)' }}>
            <div className="flex ac jb mb-8">
              <div className="flex ac gap-8">
                <span className="strong" style={{ fontSize: 13 }}>{t(sub.tk)}</span>
                {subItems.length > 0 && <span className="dim tnum" style={{ fontSize: 11 }}>{subDone}/{subItems.length}</span>}
              </div>
              {subItems.length === 0 && (
                <Btn variant="soft" size="sm" icon="upload" onClick={(e) => { e.stopPropagation(); (openUpload || (() => openModal('upload', { folderK: folder.tk })))(folder.tk); }}>{t('c.upload')}</Btn>
              )}
            </div>
            {subItems.length === 0 && <div className="muted" style={{ fontSize: 12.5, padding: '2px 0 0' }}>{t('doc.noDocs')}</div>}
            {subItems.length > 0 && (
              <div className="flex col gap-6">
                {subItems.map((it, i) => (
                  <div key={it.source + '-' + (it.tk || it.label || i)} className={'flex ac gap-10 ' + (it.url ? 'pointer' : '')} onClick={() => it.url && onOpenDocument(it)} style={{ padding: '8px 10px', border: '1px solid var(--line-2)', borderRadius: 8 }}>
                    <div className="l-ic" style={it.ok ? { width: 28, height: 28, background: 'var(--ok-bg)', color: 'var(--ok)' } : { width: 28, height: 28, background: 'var(--surface-3)', color: 'var(--ink-500)' }}>
                      <Icon name={it.ok ? 'check' : (it.ic || 'file-warning')} size={14} />
                    </div>
                    <div className="grow" style={{ minWidth: 0 }}>
                      <div className="nm" style={{ fontSize: 13 }}>{(() => {
                        if (it.tk) {
                          const tr = t('doc.' + it.tk);
                          if (tr && tr !== 'doc.' + it.tk) return tr;
                        }
                        return it.label || '-';
                      })()}</div>
                      <div className="dim" style={{ fontSize: 11.5 }}>
                        {it.origin === 'yeye' ? t('doc.byYeYe') : (it.meta || (it.ok ? t('doc.uploaded') : t('doc.awaiting')))}
                      </div>
                    </div>
                    {it.origin === 'yeye' && <Badge tone="info" dot>{t('doc.badgeYeYe')}</Badge>}
                    {it.url && (
                      <Btn variant="ghost" size="sm" icon="eye" onClick={(e) => { e.stopPropagation(); onOpenDocument(it); }}>{t('c.open')}</Btn>
                    )}
                    {it.approved ? (
                      <>
                        <Badge tone="ok">{t('docApprove.approved')}{it.approvedAt ? ` · ${docgenApprovalDate({ approvedAt: it.approvedAt })}` : ''}</Badge>
                        <Btn variant="ghost" size="sm" icon="undo-2" disabled={unapproveBusy} onClick={(e) => { e.stopPropagation(); onUnapprove(it); }}>{t('docApprove.unapprove')}</Btn>
                      </>
                    ) : (
                      <Btn variant="soft" size="sm" icon="upload" onClick={(e) => { e.stopPropagation(); (openUpload || (() => openModal('upload', { folderK: folder.tk })))(folder.tk); }}>{it.ok ? t('c.replace') : t('c.upload')}</Btn>
                    )}
                  </div>
                ))}
              </div>
            )}
          </div>
        );
      })}
      {folder.subs.length === 0 && directItems.length === 0 && (
        <div style={{ padding: '10px 6px', borderTop: '1px solid var(--line-2)' }}>
          <div className="muted" style={{ fontSize: 12.5 }}>{t('doc.noDocs')}</div>
        </div>
      )}
    </div>
  );
}

const DOCGEN_DIY_KEYS = [
  'blue_card_diy', 'blue_card_ext_diy', 'emp_card_diy', 'emp_card_ext_diy', 'eu_temp_residence_diy',
  'eu_perm_residence_diy', 'trade_cz_diy', 'pausalni_tax_registration_diy',
  'sro_establishment_diy', 'eu_vat_registration_diy', 'eori_registration_diy',
  'vehicle_registration_diy', 'vehicle_registration_non_eu_diy', 'vehicle_inspection_diy',
];

function diyCatalogOffer(key) {
  const services = (window.YEYE && window.YEYE.services) || [];
  for (const service of services) {
    const options = service.family && Array.isArray(service.tabs) ? service.tabs : [service];
    for (const option of options) {
      if (option.k === key) return { service, offer: option };
      const variant = (option.variants || []).find((item) => item.k === key);
      if (variant) return { service, offer: variant, option };
    }
  }
  return null;
}

function diyOfferName(found, t, lang) {
  if (!found) return '';
  const optionLabel = found.option && found.option.label;
  if (optionLabel && typeof optionLabel === 'object') return optionLabel[lang] || optionLabel.en || optionLabel.tr || optionLabel.cs;
  return svcName(found.service, t, lang);
}

function diyCartItem(key, t, lang) {
  const found = diyCatalogOffer(key);
  const product = ((window.YEYE && window.YEYE.marketplaceProducts) || []).find((item) => (
    item.key === key || (item.serviceKeys || []).includes(key)
  ));
  const variant = product && (product.variants || []).find((item) => item.serviceKey === key);
  if (!found) return null;
  const name = diyOfferName(found, t, lang);
  const priceMatch = String(found.offer.price || found.offer.priceLabel || '').match(/€\s*([0-9]+(?:[.,][0-9]+)?)/);
  return {
	    productKey: product ? product.key : key,
	    priceId: (variant && variant.priceId) || (product && product.priceId) || found.offer.priceId || null,
	    priceIds: (variant && variant.priceIds) || (product && product.priceIds) || {},
	    amounts: (variant && variant.amounts) || (product && product.amounts) || {},
	    productId: (variant && variant.productId) || (product && product.productId) || found.offer.productId || null,
	    serviceKey: key,
	    name,
    variantKey: (variant && variant.key) || 'diy',
    variantLabel: (variant && t(variant.labelTk)) || t('svc.variant.diy'),
    uiPrice: variant ? variant.uiPrice : Number((priceMatch && priceMatch[1] || '0').replace(',', '.')),
    uiCurrency: (variant && variant.uiCurrency) || 'EUR',
    qty: 1,
  };
}

function diySuggestionIcon(key) {
  if (/^(blue|emp)/.test(key)) return 'briefcase';
  if (/^(trade|pausalni|sro|eu_vat|eori)/.test(key)) return 'building-2';
  if (/^vehicle/.test(key)) return 'car';
  if (/^(eu_temp|eu_perm)/.test(key)) return 'home';
  if (/^translation/.test(key)) return 'languages';
  if (/^accommodation/.test(key)) return 'home';
  return 'id-card';
}

function DocGenerationSuggestions({ contact, purchasedServices }) {
  const { t, lang, currency } = useT();
  const livePaid = window.YEYE_PURCHASES && window.YEYE_PURCHASES.getPaidServices
    ? window.YEYE_PURCHASES.getPaidServices(contact || {})
    : [];
  const paid = livePaid.concat(purchasedServices || []);
  const ownedRaw = new Set((paid || []).map((item) => String(item && (item.k || item.key || item.serviceKey || item.productKey) || '').toLowerCase()).filter(Boolean));
  const normalize = typeof window.YEYE_NORMALIZE_SERVICE_KEY === 'function' ? window.YEYE_NORMALIZE_SERVICE_KEY : (key) => key;
  const ownedBases = new Set(Array.from(ownedRaw).map(normalize));
  const baseVariants = { blue_card_ext_diy: 'blue_card', emp_card_ext_diy: 'emp_card' };
  const suggestions = DOCGEN_DIY_KEYS.filter((key) => (
    !ownedRaw.has(key) && !ownedBases.has(normalize(key)) && !ownedBases.has(baseVariants[key])
  )).map((key) => ({ key, found: diyCatalogOffer(key), cartItem: diyCartItem(key, t, lang) }))
    .filter((item) => item.found && item.cartItem)
    .slice(0, 4);
  if (!suggestions.length) return null;
  return (
    <div className="docgen-suggestions">
      <div className="docgen-suggestions-title"><span className="eyebrow">{t('docgen.suggestions')}</span></div>
      <div className="docgen-suggestions-grid">
        {suggestions.map(({ key, found, cartItem }) => {
          const price = window.YEYE_CART_MONEY
            ? window.YEYE_CART_MONEY(window.YEYE_CART_CONVERT ? window.YEYE_CART_CONVERT(cartItem.uiPrice, cartItem.uiCurrency, currency) : cartItem.uiPrice, currency)
            : `${cartItem.uiPrice} ${cartItem.uiCurrency}`;
          const cadence = String(found.offer.u || found.service.u || '');
          const cadenceLabel = /month|monthly|aylık|měs/i.test(cadence) ? t('docgen.monthly') : t('docgen.oneTime');
          return (
            <div key={key} className="docgen-suggestion-card">
              <span className="docgen-suggestion-icon"><Icon name={diySuggestionIcon(key)} size={16} /></span>
              <div className="docgen-suggestion-name">{diyOfferName(found, t, lang)}</div>
              <div className="docgen-suggestion-footer">
                <span className="docgen-suggestion-price tnum">{price}<small>{cadenceLabel}</small></span>
                <button className="docgen-suggestion-add" onClick={() => {
                  window.YEYE_CART.addItem(cartItem);
                  if (window.YEYE_TOAST) window.YEYE_TOAST(t('cart.added'));
                  if (window.YEYE_CART_OPEN) window.YEYE_CART_OPEN();
                }}><Icon name="plus" size={13} />{t('c.add')}</button>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

function DocGenerationModal({ option, contact, purchasedServices, onGenerated, onCompleteProfile, onClose, onResult }) {
  const { t } = useT();
  const [phase, setPhase] = React.useState('running');
  const [messageIndex, setMessageIndex] = React.useState(0);
  const [messageVisible, setMessageVisible] = React.useState(true);
  const [longRunning, setLongRunning] = React.useState(false);
  const [result, setResult] = React.useState(null);
  const [error, setError] = React.useState('');
  const attemptRef = React.useRef(0);
  const timersRef = React.useRef([]);

  const generate = React.useCallback(async () => {
    const attempt = attemptRef.current + 1;
    attemptRef.current = attempt;
    timersRef.current.forEach(clearTimeout);
    timersRef.current = [];
    setPhase('running'); setMessageIndex(0); setMessageVisible(true); setLongRunning(false); setError(''); setResult(null);
    [1, 2].forEach((index) => {
      const changeAt = index * 5000;
      timersRef.current.push(setTimeout(() => { if (attemptRef.current === attempt) setMessageVisible(false); }, changeAt - 250));
      timersRef.current.push(setTimeout(() => {
        if (attemptRef.current === attempt) { setMessageIndex(index); setMessageVisible(true); }
      }, changeAt));
    });
    timersRef.current.push(setTimeout(() => { if (attemptRef.current === attempt) setLongRunning(true); }, 15000));
    try {
      const cfg = window.YEYE_CONFIG || {};
      const base = String(cfg.FILL_API_URL || '').replace(/\/$/, '');
      if (!base) throw new Error('backend-not-configured');
      const contactId = contact && (contact.contactId || contact.id);
      if (!contactId) throw new Error('missing-contact');
      const url = new URL(base + '/public/fill');
      url.searchParams.set('form_type', option.formType);
      if (option.documentKey) url.searchParams.set('document_key', option.documentKey);
      if (docgenExplicitTemplateId(option.templateId)) url.searchParams.set('template_id', option.templateId);
      url.searchParams.set('create_pdf', 'true');
      url.searchParams.set('sync_sheet', 'false');
      url.searchParams.set('target', 'draft');
      const contactName = [contact.firstName, contact.lastName].filter(Boolean).join(' ') || contact.name || contact.email || '';
      const res = await fetch(url.toString(), {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', ...(cfg.FILL_API_KEY ? { 'X-API-Key': cfg.FILL_API_KEY } : {}) },
        body: JSON.stringify({
          contactId,
          contactName,
          formType: option.formType,
          documentKey: option.documentKey,
          templateIds: docgenExplicitTemplateId(option.templateId) ? [option.templateId] : undefined,
          contactFields: {},
          target: 'draft',
        }),
      });
      if (!res.ok) {
        const text = await res.text().catch(() => '');
        throw new Error('http-' + res.status + (text ? ': ' + text.slice(0, 120) : ''));
      }
      const data = await res.json().catch(() => ({}));
      const documentUrl = data.documentUrl || (data.files && data.files[0] && (data.files[0].url || data.files[0].webViewLink)) || '';
      const documentId = driveFileId(documentUrl) || data.documentId || data.primaryFileId || '';
      const placeholdersReplaced = data.placeholdersReplaced ?? (data.documents && data.documents[0] && data.documents[0].placeholdersReplaced);
      const payload = {
        ...data,
        documentUrl,
        downloadUrl: documentId ? `${base}/public/download/${encodeURIComponent(documentId)}` : documentUrl,
        folderUrl: data.folderUrl || data.folder?.url || '',
        emptyProfile: placeholdersReplaced === 0,
      };
      if (onGenerated) await Promise.resolve(onGenerated(payload));
      if (attemptRef.current !== attempt) return;
      setResult(payload); setPhase('success');
      if (onResult) onResult(payload);
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('docgen.success'));
    } catch (err) {
      if (attemptRef.current !== attempt) return;
      console.warn('DocGen failed', err);
      setError(t('docgen.failed')); setPhase('error');
    }
  }, [contact, option, onGenerated, onResult, t]);

  React.useEffect(() => {
    generate();
    return () => { attemptRef.current += 1; timersRef.current.forEach(clearTimeout); };
  }, []);

  return (
    <Modal wide icon="file-plus-2" title={t(option.titleKey || ('docgen.' + option.formType))} onClose={onClose}>
      {phase === 'running' && (
        <div className={'docgen-loading' + (longRunning ? ' is-long' : '')} role="status" aria-live="polite">
          <span className="docgen-spinner docgen-loading-spinner" />
          <div className="docgen-rotating-message" style={{ opacity: messageVisible ? 1 : 0 }}>
            {t('docgen.rotating.' + (messageIndex + 1))}
          </div>
        </div>
      )}
      {phase === 'success' && result && (
        <div style={{ textAlign: 'center' }}>
          <div style={{ width: 58, height: 58, borderRadius: 99, margin: '0 auto 12px', background: 'var(--ok-bg)', color: 'var(--ok)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><Icon name="check" size={28} stroke={3} /></div>
          <div className="h2">{t('docgen.ready')}</div>
          <div className="flex col gap-8 mt-16">
            <a className="btn btn-primary btn-lg" href={result.downloadUrl} download target="_blank" rel="noopener noreferrer"><Icon name="download" size={16} />{t('docgen.openGenerated')}</a>
          </div>
          {result.emptyProfile && (
            <div style={{ marginTop: 14, padding: 12, borderRadius: 10, background: 'var(--warn-bg)', color: 'var(--warn)', fontSize: 12.5, textAlign: 'left' }}>
              {t('docgen.emptyWarning')}{' '}
              <a href="#" onClick={(e) => { e.preventDefault(); if (onCompleteProfile) onCompleteProfile(); }} style={{ color: 'var(--warn)', fontWeight: 700, textDecoration: 'underline' }}>{t('docgen.completeProfile')}</a>
            </div>
          )}
          <DocGenerationSuggestions contact={contact} purchasedServices={purchasedServices} />
        </div>
      )}
      {phase === 'error' && (
        <div style={{ textAlign: 'center', padding: '12px 0' }}>
          <div style={{ width: 58, height: 58, borderRadius: 99, margin: '0 auto 12px', background: 'var(--bad-bg)', color: 'var(--bad)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}><Icon name="x" size={28} stroke={3} /></div>
          <div className="h3">{error}</div>
          <Btn className="mt-16" variant="primary" icon="refresh-cw" onClick={generate}>{t('docgen.retry')}</Btn>
        </div>
      )}
    </Modal>
  );
}

function DocGenerationCard({ contact, purchasedServices, onGenerated, onCompleteProfile }) {
  const { t } = useT();
  const [results, setResults] = React.useState({});
  const [activeOption, setActiveOption] = React.useState(null);
  const purchasedKeys = new Set((purchasedServices || []).map((p) => String((p && (p.k || p.key || p.serviceKey || p.productKey)) || '').toLowerCase()).filter(Boolean));
  const options = [
    { key: 'blue_card_diy', formType: 'blue_card', titleKey: 'docgen.blue_card' },
    { key: 'blue_card_ext_diy', formType: 'blue_card', titleKey: 'docgen.blue_card' },
    { key: 'emp_card_diy', formType: 'employee_card_form', titleKey: 'docgen.emp_card', documentKey: 'application_form', templateId: '1MxVyb3_4epmtxYA91DSccnlDxtY0ByMC' },
    { key: 'emp_card_ext_diy', formType: 'employee_card_form', titleKey: 'docgen.emp_card', documentKey: 'application_form', templateId: '1MxVyb3_4epmtxYA91DSccnlDxtY0ByMC' },
  ].filter((opt) => purchasedKeys.has(opt.key));
  if (!options.length) return null;
  return (
    <>
      <Card className="mb-16">
        <CardHead icon="file-plus-2" title={t('docgen.title')} sub={t('docgen.sub')} />
        <div className="card-bd">
          <div style={{ display: 'grid', gap: 10 }}>
            {options.map((opt) => (
              <div key={opt.key} className="flex ac wrap gap-10" style={{ padding: 12, border: '1px solid var(--line)', borderRadius: 12, background: 'var(--surface)' }}>
                <span style={{ width: 40, height: 40, borderRadius: 10, background: 'var(--brand-50)', color: 'var(--brand-600)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}><Icon name="file-text" size={18} /></span>
                <div className="grow strong" style={{ minWidth: 0, fontSize: 14 }}>{t(opt.titleKey || ('docgen.' + opt.formType))}</div>
                <Btn variant="primary" icon={results[opt.key] ? 'refresh-cw' : 'wand-2'} onClick={() => setActiveOption(opt)}>
                  {results[opt.key] ? t('docgen.regenerate') : t('docgen.generate')}
                </Btn>
              </div>
            ))}
          </div>
        </div>
      </Card>
      {activeOption && <DocGenerationModal option={activeOption} contact={contact} purchasedServices={purchasedServices} onGenerated={onGenerated} onCompleteProfile={onCompleteProfile} onClose={() => setActiveOption(null)} onResult={(payload) => setResults((prev) => ({ ...prev, [activeOption.key]: payload }))} />}
    </>
  );
}

function DocumentsPage({ openModal, blankProfile, contact, onRefresh, purchasedServices, onCompleteProfile, fieldMappings }) {
  const { t, lang } = useT();
  const folders = P.folders || [];
  const [filter, setFilter] = useState('all');
  const [openFolderTk, setOpenFolderTk] = useState(null);
  const [archiveSubfolders, setArchiveSubfolders] = useState({});
  const [archiveFilesBySub, setArchiveFilesBySub] = useState({});
  const [newFolderOpen, setNewFolderOpen] = useState(false);
  const [openChecklistKey, setOpenChecklistKey] = useState(null);
  const [detailFile, setDetailFile] = useState(null);
  const [profileFormKey, setProfileFormKey] = useState('');
  const [unapproveFile, setUnapproveFile] = useState(null);
  const [unapproveBusy, setUnapproveBusy] = useState(false);
  const hasRealContact = !!contact && !blankProfile;
  const contactId = contact && contact.id;
  const tracker = window.PROCESS_TRACKER || {};
  const paidServicesFromContact = window.YEYE_PURCHASES && window.YEYE_PURCHASES.getPaidServices
    ? window.YEYE_PURCHASES.getPaidServices(contact || {})
    : [];
  const formType = tracker.detectFormType ? tracker.detectFormType(contact) : null;
  const docStatus = contact && formType && tracker.documentStatusFromContact
    ? tracker.documentStatusFromContact(contact, formType)
    : { items: tracker.DOCUMENT_CHECKLIST || [], total: (tracker.DOCUMENT_CHECKLIST || []).length, doneCount: 0 };
  const serviceFormMap = { blue_card: 'blue_card', emp_card: 'employee_card' };
  const normalizeServiceKey = typeof window.YEYE_NORMALIZE_SERVICE_KEY === 'function' ? window.YEYE_NORMALIZE_SERVICE_KEY : (key) => key;
  const purchasedChecklists = useMemo(() => {
    const rows = (purchasedServices || []).filter((row) => row && row.status !== 'cancelled');
    const seen = new Set();
    const out = [];
    rows.forEach((row) => {
      const serviceKey = normalizeServiceKey(row.k);
      const ftype = serviceFormMap[serviceKey];
      if (!ftype || seen.has(ftype)) return;
      seen.add(ftype);
      const svc = (P.services || []).find((s) => s.k === serviceKey) || { k: serviceKey };
      const raw = contact && tracker.documentStatusFromContact
        ? tracker.documentStatusFromContact(contact, ftype)
        : { items: tracker.DOCUMENT_CHECKLIST || [], total: (tracker.DOCUMENT_CHECKLIST || []).length, doneCount: 0 };
      const items = (raw.items || []).filter((doc) => doc.key !== 'application_fee');
      const status = {
        items,
        total: items.length,
        doneCount: items.filter((d) => d.completed).length,
      };
      out.push({ key: ftype, service: svc, status });
    });
    return out;
  }, [purchasedServices, contact]);
  const chkCopy = ({
    tr: { docsBtn: 'Belgelerim', close: 'Belgeleri kapat', pending: 'Bekleniyor', view: 'Görüntüle' },
    en: { docsBtn: 'My documents', close: 'Hide documents', pending: 'Pending', view: 'View' },
    cs: { docsBtn: 'Moje dokumenty', close: 'Skrýt dokumenty', pending: 'Čeká', view: 'Zobrazit' },
  })[lang] || { docsBtn: 'My documents', close: 'Hide documents', pending: 'Pending', view: 'View' };
  const checklistMap = {
    hlasenka: { folderK: 'f_relocation', subK: 'sf_mvcr_corr' },
    accommodation_paper: { folderK: 'f_accommodation_re', subK: 'sf_lease' },
    employment_contract: { folderK: 'f_relocation', subK: 'sf_employment_contract' },
    salary_document: { folderK: 'f_relocation', subK: 'sf_payslip' },
    power_of_attorney: { folderK: 'f_relocation', subK: 'sf_mvcr_corr' },
    power_of_attorney_salih: { folderK: 'f_relocation', subK: 'sf_mvcr_corr' },
    salary_certificate: { folderK: 'f_relocation', subK: 'sf_payslip' },
    accommodation_confirmation: { folderK: 'f_accommodation_re', subK: 'sf_lease' },
    application_form: { folderK: 'f_relocation', subK: 'sf_blue_employee' },
    criminal_record_apostilled: { folderK: 'f_identity', subK: 'sf_criminal_record' },
    diploma_apostilled: { folderK: 'f_education', subK: 'sf_diploma' },
    certificates: { folderK: 'f_education', subK: 'sf_diploma' },
    passport_visa_copy: { folderK: 'f_identity', subK: 'sf_passport' },
    biometric_photo: { folderK: 'f_identity', subK: 'sf_passport' },
    application_fee: { folderK: 'f_relocation', subK: 'sf_blue_employee' },
  };
  const displayFolders = useMemo(() => {
    const standardNames = new Set(folders.map((folder) => folderTkToSubName(folder.tk)));
    const extras = Object.keys(archiveSubfolders)
      .filter((name) => !standardNames.has(name) && !subNameToFolderTk(name))
      .sort((a, b) => a.localeCompare(b))
      .map((name) => ({
        tk: 'archive_' + name.toLowerCase().replace(/[^a-z0-9]+/g, '_'),
        label: name,
        archiveName: name,
        ic: 'folder',
        subs: [],
      }));
    return folders.concat(extras);
  }, [folders, archiveSubfolders]);
  async function refreshArchiveSubfolders() {
    if (!hasRealContact || !contactId || !window.YEYE_BACKEND) return;
    try {
      await window.YEYE_BACKEND.ensureArchiveSubfolders(contactId);
      const res = await window.YEYE_BACKEND.listArchiveSubfolders(contactId);
      const byName = {};
      ((res && res.subfolders) || []).forEach((sub) => { byName[sub.name] = sub; });
      setArchiveSubfolders(byName);
    } catch (e) {
      console.warn('Archive subfolder sync failed.', e);
    }
  }
  async function loadArchiveFiles(folderTk, { force = false } = {}) {
    if (!hasRealContact || !contactId || !window.YEYE_BACKEND) return;
    const folder = displayFolders.find((item) => item.tk === folderTk);
    const primarySubName = folderTkToSubName(folderTk, folder);
    if (!primarySubName) return;
    const subNames = [primarySubName].concat(
      Object.keys(archiveSubfolders).filter((name) => subNameToFolderTk(name) === folderTk && name !== primarySubName)
    );
    await Promise.all(subNames.map(async (subName) => {
      if (!force && archiveFilesBySub[subName]) return;
      try {
        const res = await window.YEYE_BACKEND.listArchiveFiles(contactId, subName);
        setArchiveFilesBySub((prev) => ({ ...prev, [subName]: (res && res.files) || [] }));
      } catch (e) {
        console.warn('Archive file list failed.', e);
        setArchiveFilesBySub((prev) => ({ ...prev, [subName]: [] }));
      }
    }));
  }
  const allItems = useMemo(() => {
    const archiveFiles = Object.values(archiveFilesBySub).flat();
    const archiveFileByUrl = new Map(archiveFiles
      .filter((file) => file && file.webViewLink)
      .map((file) => [String(file.webViewLink), file]));
    const checklistItems = (docStatus.items || [])
      .filter((doc) => doc.key !== 'application_fee')
      .map((doc) => {
        const mapped = checklistMap[doc.key] || {};
        const promotedFile = archiveFileByUrl.get(String(doc.url || ''));
        const promotionProps = promotedFile?.appProperties || {};
        return {
          source: 'checklist',
          tk: doc.key,
          label: doc.label,
          ok: !!(doc.completed || doc.url),
          url: doc.url,
          webViewLink: doc.url,
          meta: doc.url ? t('doc.uploaded') : '',
          origin: doc.url ? 'yeye' : undefined,
          folderK: mapped.folderK,
          subK: mapped.subK,
          approved: !!promotionProps.source_doc_id,
          approvedAt: promotedFile?.modifiedTime || promotedFile?.createdTime || '',
          sourceDocId: promotionProps.source_doc_id || '',
          processType: promotionProps.yeye_process_type || docgenProcessTypeForDocumentKey(doc.key, doc.key === 'application_form' ? formType : ''),
          docKey: promotionProps.yeye_document_key || doc.key,
          archiveFileId: promotedFile?.id || '',
        };
      })
      .filter((doc) => doc.folderK && doc.subK);
    const checklistUrls = new Set(checklistItems.filter((it) => it.url).map((it) => String(it.url)));
    const driveItems = Object.keys(archiveFilesBySub).flatMap((subName) => {
      const customFolder = displayFolders.find((folder) => folder.archiveName === subName);
      const folderK = subNameToFolderTk(subName) || (customFolder && customFolder.tk);
      if (!folderK) return [];
      return (archiveFilesBySub[subName] || [])
        .filter((file) => !checklistUrls.has(String(file.webViewLink || '')))
        .map((file) => {
          const props = (file && file.appProperties) || {};
          const docKey = String(props.yeye_document_key || '').trim();
          const mapped = docKey ? (checklistMap[docKey] || null) : null;
          const resolvedSubK = (mapped && mapped.folderK === folderK && mapped.subK) || null;
          return {
            source: 'drive',
            id: file.id,
            tk: docKey || undefined,
            label: file.name,
            ok: true,
            url: file.webViewLink,
            webViewLink: file.webViewLink,
            meta: driveFileMeta(file),
            folderK,
            subK: resolvedSubK || null,
            ic: String(file.mimeType || '').indexOf('image/') === 0 ? 'image' : 'file-text',
            approved: !!props.source_doc_id,
            approvedAt: file.modifiedTime || file.createdTime || '',
            sourceDocId: props.source_doc_id || '',
            processType: props.yeye_process_type || docgenProcessTypeForDocumentKey(docKey, docKey === 'application_form' ? formType : ''),
            docKey: props.yeye_document_key || docKey,
            archiveFileId: file.id || '',
          };
        })
        .filter((it) => it.subK);
    });
    return checklistItems.concat(driveItems)
      .sort((a, b) => (
        (a.folderK || '').localeCompare(b.folderK || '') ||
        (a.subK || '').localeCompare(b.subK || '')
      ));
  }, [docStatus.items, archiveFilesBySub, displayFolders, t]);
  const profilePrompts = useMemo(() => paidApplicationFormKeys(paidServicesFromContact, contact).map((key) => {
    const rows = window.YEYE_FIELD_MAPPING ? window.YEYE_FIELD_MAPPING.forForm(fieldMappings || [], key, contact || {}) : [];
    return { key, rows, missing: rows.filter((row) => !row.value).length };
  }).filter((item) => item.rows.length && item.missing > 0), [contact, fieldMappings]);
  React.useEffect(() => {
    if (!onRefresh) return undefined;
    const doRefresh = () => Promise.resolve(onRefresh()).catch(() => {});
    doRefresh();
    const interval = setInterval(doRefresh, 60000);
    const onVisible = () => { if (!document.hidden) doRefresh(); };
    document.addEventListener('visibilitychange', onVisible);
    window.addEventListener('focus', doRefresh);
    return () => {
      clearInterval(interval);
      document.removeEventListener('visibilitychange', onVisible);
      window.removeEventListener('focus', doRefresh);
    };
  }, [onRefresh]);
  React.useEffect(() => {
    if (!hasRealContact || !contactId) return undefined;
    refreshArchiveSubfolders();
    return undefined;
  }, [hasRealContact, contactId]);
  React.useEffect(() => {
    if (openFolderTk) loadArchiveFiles(openFolderTk);
  }, [openFolderTk, contactId, archiveSubfolders]);
  const openUpload = (folderK) => {
    const folder = displayFolders.find((item) => item.tk === folderK);
    const subName = folder ? folderTkToSubName(folderK, folder) : '';
    openModal('upload', {
      contactId,
      folderK,
      subName,
      onUploaded: async () => {
        await Promise.resolve(refreshArchiveSubfolders());
        if (folderK) await Promise.resolve(loadArchiveFiles(folderK, { force: true }));
        if (onRefresh) await Promise.resolve(onRefresh());
        if (window.YEYE_TOAST) window.YEYE_TOAST(t('doc.uploaded'));
      },
    });
  };
  const unapproveDocument = async () => {
    if (!unapproveFile?.sourceDocId || !contactId || unapproveBusy) return;
    const cfg = window.YEYE_CONFIG || {};
    const base = String(cfg.FILL_API_URL || '').replace(/\/$/, '');
    if (!base) return;
    setUnapproveBusy(true);
    try {
      const response = await fetch(`${base}/public/contacts/${encodeURIComponent(contactId)}/documents/${encodeURIComponent(unapproveFile.sourceDocId)}/unpromote`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', ...(cfg.FILL_API_KEY ? { 'X-API-Key': cfg.FILL_API_KEY } : {}) },
        body: JSON.stringify({ docKey: unapproveFile.docKey || '', processType: unapproveFile.processType || '' }),
      });
      if (!response.ok) throw new Error('http-' + response.status);
      setArchiveFilesBySub((previous) => Object.fromEntries(Object.entries(previous).map(([subName, files]) => [
        subName,
        (files || []).filter((file) => file.id !== unapproveFile.archiveFileId),
      ])));
      setUnapproveFile(null);
      if (onRefresh) await Promise.resolve(onRefresh());
      window.dispatchEvent(new CustomEvent('yeye:documents-changed', { detail: { contactId, action: 'unapproved' } }));
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('docgen.draftDocs.undoSuccess'));
    } catch (error) {
      console.warn('Document unapproval failed', error);
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('docgen.draftDocs.errorGeneric'));
    } finally {
      setUnapproveBusy(false);
    }
  };
  return (
    <>
      <PageHead title={t('nav.documents')} sub={t('sub.documents')}
        actions={<><Btn variant="ghost" icon="folder-plus" onClick={() => setNewFolderOpen(true)}>{t('c.newFolder')}</Btn><Btn variant="primary" icon="upload" onClick={() => openUpload(openFolderTk)}>{t('c.upload')}</Btn></>} />
      {profilePrompts.map((prompt) => (
        <Card className="mb-16" key={prompt.key} style={{ borderColor: 'var(--brand-200)', background: 'var(--brand-50)' }}>
          <div className="card-bd flex ac jb wrap gap-12">
            <div>
              <div className="strong" style={{ fontSize: 14 }}>{t('profile.context.title').replace('{service}', applicationFormLabel(prompt.key, lang)).replace('{count}', prompt.missing)}</div>
              <div className="muted" style={{ fontSize: 12.5, marginTop: 4 }}>{t('profile.context.body')}</div>
            </div>
            <Btn variant="primary" icon="clipboard-list" onClick={() => setProfileFormKey(prompt.key)}>{t('profile.context.cta')}</Btn>
          </div>
        </Card>
      ))}
      <DocGenerationCard contact={contact} purchasedServices={purchasedServices} onCompleteProfile={onCompleteProfile} onGenerated={async () => {
        setArchiveFilesBySub({});
        await Promise.resolve(refreshArchiveSubfolders());
        if (onRefresh) await Promise.resolve(onRefresh());
      }} />
      {allItems.length === 0 && <div className="muted mb-12" style={{ fontSize: 13 }}>{t('doc.noDocs')}</div>}
      <div className="grid g-12" style={{ gap: 14, gridTemplateColumns: 'repeat(auto-fit, minmax(260px, 1fr))' }}>
        {displayFolders.map((folder) => (
          <FolderArchiveCard
            folder={folder}
            allItems={allItems}
            archiveSubfolders={archiveSubfolders}
            openFolderTk={openFolderTk}
            onToggle={setOpenFolderTk}
            key={folder.tk}
          />
        ))}
      </div>
      {openFolderTk && (
        <Modal wide icon="folder-open" title={(displayFolders.find((f) => f.tk === openFolderTk) || {}).label || t(openFolderTk)} onClose={() => setOpenFolderTk(null)}>
          <FolderExpandedBody folder={displayFolders.find((f) => f.tk === openFolderTk)} allItems={allItems} filter={filter} openModal={(kind, data) => openUpload(data && data.folderK)} openUpload={openUpload} onOpenDocument={(file) => { setOpenFolderTk(null); setDetailFile(file); }} onUnapprove={setUnapproveFile} unapproveBusy={unapproveBusy} />
        </Modal>
      )}
      {unapproveFile && (
        <Modal
          icon="undo-2"
          title={t('docApprove.unapprove')}
          onClose={() => !unapproveBusy && setUnapproveFile(null)}
          footer={<><Btn variant="ghost" disabled={unapproveBusy} onClick={() => setUnapproveFile(null)}>{t('c.cancel')}</Btn><Btn variant="primary" disabled={unapproveBusy} onClick={unapproveDocument}>{t('c.confirm')}</Btn></>}
        >
          <div style={{ lineHeight: 1.6 }}>{t('docApprove.unapproveConfirm')}</div>
        </Modal>
      )}
      {openChecklistKey && (() => {
        const checklist = purchasedChecklists.find((item) => item.key === openChecklistKey);
        if (!checklist) return null;
        return (
          <Modal wide icon="list-checks" title={svcName(checklist.service, t, lang)} sub={`${checklist.status.doneCount || 0}/${checklist.status.total || 0} ${t('sec.complete')}`} onClose={() => setOpenChecklistKey(null)}>
            <div className="rowlist">
              {(checklist.status.items || []).map((doc) => (
                <div className={'lrow ' + (doc.url ? 'pointer' : '')} key={doc.key} onClick={() => {
                  if (!doc.url) return;
                  setOpenChecklistKey(null);
                  setDetailFile({ label: doc.label, url: doc.url, webViewLink: doc.url, meta: t('doc.uploaded'), ic: 'file-text' });
                }}>
                  <div className="l-ic" style={doc.completed ? { background: 'var(--ok-bg)', color: 'var(--ok)' } : { background: 'var(--surface-3)', color: 'var(--ink-300)' }}>
                    <Icon name={doc.completed ? 'file-check-2' : 'file'} size={17} />
                  </div>
                  <div className="l-bd"><div className="l-t">{doc.label}</div></div>
                  {doc.url ? <Btn variant="quiet" size="sm" icon="eye">{chkCopy.view}</Btn> : doc.completed ? <Badge tone="ok" dot>{t('c.done')}</Badge> : <Badge tone="neut">{chkCopy.pending}</Badge>}
                </div>
              ))}
            </div>
          </Modal>
        );
      })()}
      {newFolderOpen && <NewFolderModal contactId={contactId} onClose={() => setNewFolderOpen(false)} onCreated={() => { setNewFolderOpen(false); refreshArchiveSubfolders(); }} />}
      {detailFile && <DocumentDetailModal file={detailFile} onClose={() => setDetailFile(null)} />}
      {profileFormKey && (
        <ApplicationFormModal
          formKey={profileFormKey}
          contact={contact}
          rows={window.YEYE_FIELD_MAPPING ? window.YEYE_FIELD_MAPPING.forForm(fieldMappings || [], profileFormKey, contact || {}) : []}
          lang={lang}
          t={t}
          onClose={() => setProfileFormKey('')}
          onSaved={() => { setProfileFormKey(''); if (onRefresh) onRefresh(); }}
          applyOptimisticUpdate={() => {}}
        />
      )}
    </>
  );
}

function NewFolderModal({ contactId, onClose, onCreated }) {
  const { t } = useT();
  const [name, setName] = useState('');
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState('');
  async function submit() {
    const clean = name.trim();
    if (!clean || !contactId || saving) return;
    setSaving(true); setError('');
    try {
      await window.YEYE_BACKEND.createArchiveSubfolder({ contactId, name: clean });
      onCreated();
    } catch (e) {
      console.warn('Create archive subfolder failed.', e);
      setError(e.message || 'create-failed');
      setSaving(false);
    }
  }
  return (
    <Modal icon="folder-plus" title={t('doc.newFolderTitle')} sub={t('doc.archiveSubfolder')} onClose={onClose}
      footer={<><Btn variant="ghost" onClick={onClose}>{t('c.cancel')}</Btn><Btn variant="primary" icon="folder-plus" onClick={submit}>{saving ? t('c.saving') : t('c.create')}</Btn></>}>
      <Field label={t('doc.folderName')}>
        <input className="input" value={name} onChange={(e) => setName(e.target.value)} autoFocus />
      </Field>
      {error && <div className="alert bad mt-12"><div className="a-ic"><Icon name="alert-triangle" size={16} /></div><div className="grow"><div className="a-t">{error}</div></div></div>}
    </Modal>
  );
}

/* ===== MESSAGES ===== */
function MessagesPage() {
  const { t } = useT();
  const threads = [
    { whoK: 'immigration', av: 'IM', c: 'brand', lastK: 'm.appt', tm: '2h', n: 2 },
    { whoK: 'support', av: 'SP', c: 'accent', lastK: 'm.doc', tm: '1d', n: 0 },
    { whoK: 'relocation', av: 'RL', c: 'neut', lastK: 'm.reloc', tm: '3d', n: 0 },
  ];
  const [active, setActive] = useState(0);
  const conv = [
    { me: false, k: 'msg.c1', t: '09:12' }, { me: true, k: 'msg.c2', t: '09:20' },
    { me: false, k: 'msg.c3', t: '09:24' }, { me: false, k: 'msg.c4', t: '2h' },
  ];
  const tone = (c) => c === 'brand' ? { bg: 'var(--brand-100)', fg: 'var(--brand-700)' } : c === 'accent' ? { bg: 'var(--accent-100)', fg: 'var(--accent-700)' } : { bg: 'var(--neut-bg)', fg: 'var(--neut)' };
  return (
    <div style={{ height: '100%' }}>
      <div className="card" style={{ display: 'flex', height: 'calc(100vh - 66px - 60px)', minHeight: 460, overflow: 'hidden' }}>
        <div style={{ width: 300, borderRight: '1px solid var(--line)', display: 'flex', flexDirection: 'column' }} className="desktop-only">
          <div style={{ padding: '16px 18px', borderBottom: '1px solid var(--line-2)' }} className="flex jb ac"><div className="h3">{t('msg.inbox')}</div><IconBtn name="square-pen" /></div>
          <div style={{ overflowY: 'auto', flex: 1 }}>
            {threads.map((th, i) => (
              <div key={i} onClick={() => setActive(i)} className="pointer" style={{ display: 'flex', gap: 11, padding: '13px 16px', borderBottom: '1px solid var(--line-2)', background: active === i ? 'var(--brand-50)' : 'transparent' }}>
                <Avatar size="md" tone={tone(th.c)}>{th.av}</Avatar>
                <div className="grow" style={{ minWidth: 0 }}><div className="flex jb"><span className="nm" style={{ fontSize: 13.5 }}>{t('who.' + th.whoK)}</span><span className="dim" style={{ fontSize: 11 }}>{th.tm}</span></div><div className="dim" style={{ fontSize: 12, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{t(th.lastK)}</div></div>
                {th.n > 0 && <span className="nav-badge">{th.n}</span>}
              </div>
            ))}
          </div>
        </div>
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0 }}>
          <div style={{ padding: '13px 20px', borderBottom: '1px solid var(--line-2)' }} className="flex ac gap-12">
            <Avatar size="md" tone={tone(threads[active].c)}>{threads[active].av}</Avatar>
            <div className="grow"><div className="strong">{t('who.' + threads[active].whoK)}</div><div className="dim flex ac gap-6" style={{ fontSize: 12 }}><i style={{ background: 'var(--ok)', width: 7, height: 7, borderRadius: 99, display: 'inline-block' }} />{t('m.reply')}</div></div>
            <IconBtn name="phone" /><IconBtn name="info" />
          </div>
          <div style={{ flex: 1, overflowY: 'auto', padding: '20px 22px', display: 'flex', flexDirection: 'column', gap: 12, background: 'var(--surface-2)' }}>
            {conv.map((m, i) => (
              <div key={i} style={{ alignSelf: m.me ? 'flex-end' : 'flex-start', maxWidth: '74%' }}>
                <div style={{ padding: '10px 14px', borderRadius: 14, fontSize: 13.5, lineHeight: 1.5, background: m.me ? 'var(--brand-500)' : '#fff', color: m.me ? '#fff' : 'var(--ink-800)', border: m.me ? 'none' : '1px solid var(--line)', borderBottomRightRadius: m.me ? 4 : 14, borderBottomLeftRadius: m.me ? 14 : 4 }}>{t(m.k)}</div>
                <div className="dim" style={{ fontSize: 10.5, marginTop: 4, textAlign: m.me ? 'right' : 'left' }}>{m.t}</div>
              </div>
            ))}
          </div>
          <div style={{ padding: '13px 16px', borderTop: '1px solid var(--line-2)' }} className="flex ac gap-10">
            <IconBtn name="paperclip" />
            <input className="input" placeholder={t('m.write')} style={{ flex: 1 }} />
            <Btn variant="primary" icon="send">{t('c.send')}</Btn>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ===== INVOICES ===== */
function invoiceAmountValue(amount) {
  const eur = String(amount || '').match(/€\s*([0-9]+(?:[.,][0-9]+)?)/);
  const n = Number((eur ? eur[1] : String(amount || '')).replace(',', '.').replace(/[^0-9.-]/g, ''));
  return Number.isFinite(n) ? n : 0;
}
function paidTotalLabel(total, currency) {
  return moneyLabel(total * (CURRENCY_RATES[currency] || 1), currency);
}
function invoiceDisplayAmount(inv, currency) {
  const eur = Number.isFinite(inv.eurAmount) ? inv.eurAmount : invoiceAmountValue(inv.amt);
  return moneyLabel(eur * (CURRENCY_RATES[currency] || 1), currency);
}
function downloadInvoice(inv, t) {
  const service = inv.serviceName || t('iv.' + inv.svcK);
  const paidAt = inv.paidAt || inv.due || '';
  const html = `<!doctype html>
<html><head><meta charset="utf-8"><title>${inv.id}</title>
<style>body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;margin:40px;color:#17202a}h1{margin:0 0 8px}.muted{color:#667085}.box{border:1px solid #d9dee7;border-radius:10px;padding:18px;margin-top:24px}table{width:100%;border-collapse:collapse;margin-top:18px}th,td{text-align:left;border-bottom:1px solid #e8ebf0;padding:12px}th{font-size:12px;text-transform:uppercase;color:#667085}.total{font-size:22px;font-weight:700;text-align:right}.paid{display:inline-block;background:#e8f7ef;color:#147a3f;border-radius:999px;padding:6px 10px;font-weight:700}</style>
</head><body>
<h1>Invoice ${inv.id}</h1>
<div class="muted">YeYe Expats Centre</div>
<div class="box"><span class="paid">${t('iv.invoicePaid')}</span><p>${t('iv.autoInvoice')}</p><p><strong>${t('col.paidOn')}:</strong> ${paidAt}</p></div>
<table><thead><tr><th>${t('nav.services')}</th><th>${t('col.amount')}</th></tr></thead><tbody><tr><td>${service}</td><td>${inv.amt}</td></tr></tbody></table>
<p class="total">${inv.amt}</p>
</body></html>`;
  const blob = new Blob([html], { type: 'text/html;charset=utf-8' });
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = inv.id + '.html';
  document.body.appendChild(a);
  a.click();
  setTimeout(() => { URL.revokeObjectURL(a.href); a.remove(); }, 0);
}
function saveInvoicePdf(blob, number) {
  const safeNumber = String(number || 'invoice').replace(/[^a-z0-9._-]+/gi, '-');
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'invoice-' + safeNumber + '.pdf';
  document.body.appendChild(a);
  a.click();
  setTimeout(() => { URL.revokeObjectURL(a.href); a.remove(); }, 0);
}
async function downloadOriginalInvoice(inv, invoicePdfUrl, number, t) {
  if (!invoicePdfUrl) {
    downloadInvoice(inv, t);
    return;
  }
  try {
    if (!window.YEYE_BACKEND || !window.YEYE_BACKEND.fetchInvoicePdf) throw new Error('invoice-pdf-fetch-unavailable');
    const blob = await window.YEYE_BACKEND.fetchInvoicePdf(invoicePdfUrl);
    saveInvoicePdf(blob, number);
  } catch (_) {
    const readBase = String((window.YEYE_CONFIG && (window.YEYE_CONFIG.READ_API_URL || window.YEYE_CONFIG.GHL_READ_API_URL)) || '').replace(/\/$/, '');
    if (readBase && String(invoicePdfUrl).startsWith(readBase + '/')) {
      downloadInvoice(inv, t);
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('invoices.originalUnavailable'));
      return;
    }
    const anchor = document.createElement('a');
    anchor.href = invoicePdfUrl;
    anchor.target = '_blank';
    anchor.rel = 'noopener noreferrer';
    anchor.download = 'invoice-' + String(number || 'invoice').replace(/[^a-z0-9._-]+/gi, '-') + '.pdf';
    document.body.appendChild(anchor);
    anchor.click();
    anchor.remove();
  }
}
function InvoicesPage({ contact, go, openModal, onCheckout }) {
  const { t, currency } = useT();
  const invoiceUtils = window.YEYE_INVOICES;
  const cart = useCartState();
  const [invoices, setInvoices] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');
  const [refreshing, setRefreshing] = useState(false);
  const [sendingId, setSendingId] = useState('');
  const [cancellingId, setCancellingId] = useState('');
  const contactId = contact && contact.contactId;
  const loadInvoices = async (silent) => {
    if (!contactId) {
      setInvoices([]);
      setLoading(false);
      return;
    }
    if (silent) setRefreshing(true);
    else setLoading(true);
    try {
      const result = await window.YEYE_BACKEND.listInvoices(contactId);
      setInvoices(invoiceUtils.normalizeList(result));
      setError('');
    } catch (err) {
      setError((err && err.message) || t('checkout.error'));
    } finally {
      setLoading(false);
      setRefreshing(false);
    }
  };
  useEffect(() => { loadInvoices(false); }, [contactId]);
  useEffect(() => {
    const poll = () => { if (document.visibilityState === 'visible') loadInvoices(true); };
    const timer = window.setInterval(poll, 30000);
    const onVisibility = () => { if (document.visibilityState === 'visible') loadInvoices(true); };
    document.addEventListener('visibilitychange', onVisibility);
    return () => {
      window.clearInterval(timer);
      document.removeEventListener('visibilitychange', onVisibility);
    };
  }, [contactId]);
  const [cancelledIds, setCancelledIds] = useState(() => {
    return invoiceUtils.cancelledIds();
  });
  const cancelInvoice = async (invoice) => {
    const id = String(invoiceUtils.idFor(invoice) || '');
    if (!id || cancellingId) return;
    if (!window.confirm(t('invoices.cancelConfirm'))) return;
    setCancellingId(id);
    try {
      await window.YEYE_BACKEND.voidInvoice(id);
      setCancelledIds((prev) => {
        const next = new Set(prev);
        next.add(id);
        try { localStorage.setItem(invoiceUtils.CANCELLED_KEY, JSON.stringify([...next])); } catch (_) {}
        return next;
      });
      setInvoices((rows) => rows.map((row) => (
        String(invoiceUtils.idFor(row) || '') === id
          ? Object.assign({}, row, { status: 'void' })
          : row
      )));
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('invoices.cancelled'));
    } catch (_) {
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('invoices.cancelError'));
    } finally {
      setCancellingId('');
    }
  };
  const visibleInvoices = useMemo(
    () => invoiceUtils.visibleInvoices(invoices, contact, cancelledIds),
    [invoices, contact, cancelledIds]
  );
  const resend = async (invoice) => {
    const id = invoiceUtils.idFor(invoice);
    if (!id || sendingId) return;
    setSendingId(id);
    try {
      const result = await window.YEYE_BACKEND.sendInvoice(id);
      if (result && result.invoiceUrl) {
        invoiceUtils.rememberUrl(id, result.invoiceUrl);
        setInvoices((rows) => rows.map((row) => (
          String(invoiceUtils.idFor(row) || '') === String(id)
            ? Object.assign({}, row, { invoiceUrl: result.invoiceUrl })
            : row
        )));
      }
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('invoices.resent'));
      await loadInvoices(true);
    } catch (_) {
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('invoices.resendError'));
    } finally {
      setSendingId('');
    }
  };
  const paidInvoices = visibleInvoices.filter((invoice) => invoiceUtils.statusFor(invoice) === 'paid');
  const readyInvoices = visibleInvoices.filter((invoice) => invoiceUtils.statusFor(invoice) !== 'draft');
  const paidTotal = paidInvoices.reduce((sum, invoice) => sum + invoiceUtils.amountInCurrency(invoice, currency), 0);
  const cartTotals = window.YEYE_CART_TOTALS(cart.items, currency);

  return (
    <>
      <PageHead title={t('invoices.title')} sub={t('sub.invoices')}
        actions={<>
          <Btn variant="ghost" icon="life-buoy" onClick={() => openModal && openModal('ticket', { catK: 'billing', subject: t('ticket.itInvoiceSubject') })}>{t('c.openItTicket')}</Btn>
        </>} />
      {error && <Alert tone="bad" title={t('checkout.error')}>{error}</Alert>}
      {cart.items.length > 0 && (
        <Card className="card-pad mb-20" style={{ border: '1px solid var(--brand-200)', background: 'var(--brand-50)' }}>
          <div className="flex jb ac gap-12 wrap mb-12">
            <div className="flex ac gap-10">
              <div className="hd-ic" style={{ background: 'var(--brand-100)', color: 'var(--brand-700)' }}><Icon name="shopping-cart" size={18} /></div>
              <div className="h3">{t('cart.pending')}</div>
              <Badge tone="info">{t('cart.pendingItems').replace('{n}', cart.items.length)}</Badge>
            </div>
          </div>
          <div className="flex col gap-8">
            {cart.items.map((item) => (
              <div className="flex jb ac gap-12 wrap" key={item.id} style={{ padding: '8px 0', borderBottom: '1px solid var(--brand-100)' }}>
                <div><span className="strong">{item.name}</span>{item.variantLabel && <span className="muted"> · {item.variantLabel}</span>}</div>
                <div className="strong tnum">{window.YEYE_CART_MONEY(window.YEYE_CART_CONVERT ? window.YEYE_CART_CONVERT(item.uiPrice, item.uiCurrency, currency) : (Number(item.uiPrice) || 0), currency)} × {item.qty}</div>
              </div>
            ))}
          </div>
          <div className="flex jb ac gap-12 wrap mt-16">
            <div>
              <div className="eyebrow">{t('cart.subtotal')}</div>
              <div className="flex gap-12 wrap">{Object.keys(cartTotals).map((code) => <strong className="tnum" key={code}>{window.YEYE_CART_MONEY(cartTotals[code], code)}</strong>)}</div>
            </div>
            <div className="flex gap-8 wrap">
              <Btn variant="ghost" onClick={() => go('services')}>{t('cart.continueShopping')}</Btn>
              <Btn variant="primary" iconR="arrow-right" onClick={onCheckout}>{t('cart.payNow')}</Btn>
            </div>
          </div>
        </Card>
      )}
      <div className="grid g-12 mb-20" style={{ gap: 16 }}>
        <div className="col-4"><Stat icon="check-check" value={moneyLabel(paidTotal, currency)} label={t('iv.totalPaid')} /></div>
        <div className="col-4"><Stat icon="package-check" tone="blue" value={String(paidInvoices.length)} label={t('iv.paidServices')} /></div>
        <div className="col-4"><Stat icon="receipt-text" tone="amber" value={String(readyInvoices.length)} label={t('iv.invoiceReady')} /></div>
      </div>
      <div className="tbl-wrap mt-16">
        <table className="tbl">
          <thead><tr><th>{t('col.invoice')}</th><th>{t('nav.services')}</th><th>{t('col.amount')}</th><th>{t('col.paidOn')}</th><th>{t('col.status')}</th><th></th></tr></thead>
          <tbody>
            {loading ? (
              <tr><td colSpan="6"><div className="muted" style={{ padding: 18, textAlign: 'center' }}>{t('invoices.loading')}</div></td></tr>
            ) : visibleInvoices.length === 0 ? (
              <tr><td colSpan="6"><div className="muted" style={{ padding: 18, textAlign: 'center' }}>—</div></td></tr>
            ) : visibleInvoices.map((invoice) => {
              const id = invoiceUtils.idFor(invoice);
              const status = invoiceUtils.statusFor(invoice);
              const number = invoice.invoiceNumber || invoice.number || (id ? String(id).slice(-8) : '—');
              const invoiceUrl = invoice.invoiceUrl || invoice.url || invoice.paymentUrl;
              const invoicePdfUrl = invoice.pdfUrl || invoice.downloadUrl || '';
              const lineItems = invoiceUtils.itemsOf(invoice);
              const currencyCode = invoice.currency || 'CZK';
              const fmtLine = (n) => Number(n || 0).toLocaleString(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 2 }) + ' ' + currencyCode;
              return (
                <tr key={id || number}>
                  <td className="mono nm" style={{ fontSize: 12.5 }}>#{number}</td>
                  <td className="muted">
                    {lineItems.length > 0 ? (
                      <div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
                        {lineItems.map((it, idx) => (
                          <div key={idx} style={{ fontSize: 12.5, display: 'flex', justifyContent: 'space-between', gap: 12 }}>
                            <span>{it.name || it.description || '—'}{(it.qty || it.quantity) > 1 ? ' × ' + (it.qty || it.quantity) : ''}</span>
                            <span className="tnum">{fmtLine(invoiceUtils.itemNumericAmount(it))}</span>
                          </div>
                        ))}
                      </div>
                    ) : (
                      invoiceUtils.nameFor(invoice, t('invoices.title'))
                    )}
                  </td>
                  <td className="nm tnum">{invoiceUtils.amountFor(invoice)}</td>
                  <td className="muted mono" style={{ fontSize: 12.5 }}>{invoiceUtils.dateFor(invoice)}</td>
                  <td><Badge tone={invoiceUtils.toneFor(status)} dot>{t('invoices.status.' + status)}</Badge></td>
                  <td className="txt-r">
                    <div className="flex gap-6" style={{ justifyContent: 'flex-end', flexWrap: 'nowrap' }}>
                      {status !== 'paid' && invoiceUrl && <a className="btn btn-primary btn-sm" href={invoiceUrl} target="_blank" rel="noopener noreferrer"><Icon name="credit-card" size={14} />{t('invoices.pay')}</a>}
                      {status === 'paid' && (invoicePdfUrl || invoiceUrl) ? (
                        <a className="btn btn-ghost btn-sm" href={invoicePdfUrl || invoiceUrl} target="_blank" rel="noopener noreferrer" title={t('invoices.downloadPaid')}>
                          <Icon name="download" size={14} />{t('invoices.pdf')}
                        </a>
                      ) : status !== 'paid' ? (
                        <Btn variant="ghost" size="sm" icon="download" onClick={() => { if (window.YEYE_TOAST) window.YEYE_TOAST(t('invoices.notPaidYet')); }} title={t('invoices.notPaidYet')}>
                          {t('invoices.pdf')}
                        </Btn>
                      ) : null}
                      {status === 'paid' && invoiceUrl && <a className="btn btn-ghost btn-sm" href={invoiceUrl} target="_blank" rel="noopener noreferrer" title={t('invoices.openInvoice')}><Icon name="external-link" size={14} /></a>}
                      {status !== 'paid' && <Btn variant="ghost" size="sm" icon="send" onClick={() => resend(invoice)} disabled={!id || sendingId === id} title={t('invoices.resendInvoice')} />}
                      {status !== 'paid' && status !== 'void' && <Btn variant="ghost" size="sm" icon="x" onClick={() => cancelInvoice(invoice)} disabled={!id || cancellingId === id} title={t('invoices.cancel')} />}
                    </div>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </>
  );
}

/* ===== SUPPORT ===== */
function SupportPage({ openModal, tickets, tasks, go }) {
  const { t } = useT();
  const cards = [{ ic: 'messages-square', k: 'chat' }, { ic: 'phone', k: 'call' }, { ic: 'calendar-plus', k: 'book' }];
  const CATEGORY_RE = /^\[([^\]]+)\]\s*/;
  const taskTickets = (tasks || [])
    .map((task) => {
      const title = String(task.title || '');
      const match = title.match(CATEGORY_RE);
      if (!match) return null;
      const catK = match[1].toLowerCase();
      const subject = title.replace(CATEGORY_RE, '').trim() || title;
      const done = !!task.done;
      return {
        id: task.ghlTaskId || task.id,
        subject,
        catK,
        details: task.body || task.description || task.details || '',
        st: done ? 'resolved' : 'open',
        stc: done ? 'ok' : 'info',
        upd: task.due || task.dateLabel || '—',
        source: 'ghl',
      };
    })
    .filter(Boolean);
  const localOnly = (tickets || []).filter((tk) => !tk.ghlTaskId);
  const merged = [...localOnly, ...taskTickets];
  const supportCardAction = (key) => {
    if (key === 'chat' || key === 'call') {
      if (window.YEYE_TOAST) window.YEYE_TOAST(t('sup.paidWhatsAppToast'));
      if (go) go('services');
      return;
    }
    if (key === 'book' && go) go('services');
  };
  return (
    <>
      <PageHead title={t('nav.support')} sub={t('sub.support')}
        actions={<Btn variant="primary" icon="plus" onClick={() => openModal('ticket')}>{t('c.newTicket')}</Btn>} />
      <div className="grid g-12 mb-24" style={{ gap: 14 }}>
        {cards.map((c, i) => (
          <div className="col-3" key={i}><Card className="card-pad pointer" onClick={() => supportCardAction(c.k)} style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
            <div className="hd-ic" style={{ width: 40, height: 40 }}><Icon name={c.ic} size={19} /></div>
            <div><div className="strong" style={{ fontSize: 14 }}>{t('sup.' + c.k)}</div><div className="dim" style={{ fontSize: 12 }}>{t('sup.' + c.k + 'S')}</div></div>
          </Card></div>
        ))}
      </div>
      <Card>
        <CardHead icon="life-buoy" title={t('sup.yourTickets')} sub={t('sup.yourTicketsSub')} />
        <div className="tbl-wrap" style={{ border: 'none', borderRadius: 0 }}>
          <table className="tbl">
            <thead><tr><th>{t('col.ticket')}</th><th>{t('col.subject')}</th><th>{t('col.category')}</th><th>{t('col.status')}</th><th>{t('col.updated')}</th></tr></thead>
            <tbody>
              {merged.length === 0 ? (
                <tr><td colSpan="5"><div className="muted" style={{ padding: 18, textAlign: 'center' }}>—</div></td></tr>
              ) : merged.map((tk, i) => (
                <tr key={tk.id || i} className="pointer">
                  <td className="mono nm" style={{ fontSize: 12.5 }}>{tk.id}</td>
                  <td className="nm">
                    <div>{tk.subject || t('t.' + tk.tk)}</div>
                    {tk.details && <div className="muted" style={{ fontSize: 11.5, marginTop: 3, whiteSpace: 'normal', maxWidth: 420, lineHeight: 1.4 }}>{tk.details}</div>}
                  </td>
                  <td className="muted">{t('cat.' + tk.catK)}</td>
                  <td><Badge tone={tk.stc} dot>{t('st.' + tk.st)}</Badge></td>
                  <td className="muted">{tk.upd}{tk.upd && tk.upd !== '—' ? ' ' + t('col.ago') : ''}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </Card>
    </>
  );
}

/* ===== FORM SUBMISSION ===== */
function formatFieldValue(value) {
  const text = String(value || '').trim();
  if (!text) return '';
  if (/^\d{4}-\d{2}-\d{2}T/.test(text)) return text.slice(0, 10);
  return text;
}

function contactFieldValue(contact, keys, fallback) {
  const wanted = (Array.isArray(keys) ? keys : [keys]).filter(Boolean);
  const values = contact || {};
  const raw = contact?.raw || {};
  const custom = contact?.customField || contact?.customFields || contact?.fields || raw.customField || raw.customFields || [];
  const norm = (value) => String(value || '').toLowerCase().replace(/[\s._-]+/g, '');
  for (const key of wanted) {
    const direct = values[key] ?? raw[key];
    if (direct != null && String(direct).trim() !== '') return direct;
    const stripped = String(key).replace(/^contact\./, '').replace(/^business\./, '');
    const strippedValue = values[stripped] ?? raw[stripped];
    if (strippedValue != null && String(strippedValue).trim() !== '') return strippedValue;
  }
  if (Array.isArray(custom)) {
    const found = custom.find((field) => wanted.some((key) => [
      field?.key, field?.fieldKey, field?.name, field?.id, field?.fieldId, field?.customFieldId
    ].some((candidate) => norm(candidate) === norm(key))));
    const value = found?.value ?? found?.field_value ?? found?.fieldValue;
    if (value != null && String(value).trim() !== '') return value;
  } else if (custom && typeof custom === 'object') {
    for (const key of wanted) {
      const direct = custom[key];
      if (direct != null && String(direct).trim() !== '') return direct;
      const foundKey = Object.keys(custom).find((item) => norm(item) === norm(key));
      if (foundKey && custom[foundKey] != null && String(custom[foundKey]).trim() !== '') return custom[foundKey];
    }
  }
  return fallback || '';
}

function isFilledContactValue(value) {
  const text = String(value ?? '').trim();
  return !!text && text !== '—' && text.toLowerCase() !== 'eksik';
}

function submittedFormChips(contact, formKey) {
  const tags = (contact?.tags || contact?.raw?.tags || []).map((tag) => String(tag).toLowerCase());
  const chips = [];
  const hasBlueSubmission = tags.some((tag) => /^blue[_ -]?card[_ -](form_created|submitted|payment|purchased|created)/.test(tag));
  const hasEmpSubmission = tags.some((tag) => /^employee[_ -]?card[_ -](form_created|submitted|payment|purchased|created)/.test(tag));
  if (hasBlueSubmission) chips.push({ key: 'blue_card', label: 'Blue Card', tone: 'info' });
  if (hasEmpSubmission) chips.push({ key: 'employee_card', label: 'Employee Card', tone: 'warn' });
  return chips;
}

function contactDisplayName(contact) {
  const prettyName = (value) => String(value || '').trim().split(/\s+/).filter(Boolean)
    .map((part) => part ? part.charAt(0).toLocaleUpperCase() + part.slice(1) : part)
    .join(' ');
  const name = [contactFieldValue(contact, ['firstName', 'first_name', 'contact.first_name']), contactFieldValue(contact, ['lastName', 'last_name', 'contact.last_name'])]
    .filter(Boolean).join(' ').trim() || contact?.name || contact?.email || '—';
  if (!name || name === '—' || String(name).includes('@')) return name;
  return prettyName(name);
}

function contactInitials(contact) {
  const name = contactDisplayName(contact);
  const parts = name.split(/\s+/).filter(Boolean);
  return (parts.length > 1 ? parts[0][0] + parts[1][0] : name.slice(0, 2)).toUpperCase();
}

function expandFieldLabelAbbreviations(label, lang = 'en') {
  const replacements = {
    en: { dob: 'Date of Birth', no: 'Number', nat: 'Nationality', occ: 'Occupation', pob: 'Place of Birth', poi: 'Place of Issue', doi: 'Date of Issue', exp: 'Expiry' },
    tr: { dob: 'Doğum Tarihi', no: 'Numara', nat: 'Uyruk', occ: 'Meslek', pob: 'Doğum Yeri', poi: 'Verildiği Yer', doi: 'Verildiği Tarih', exp: 'Geçerlilik' },
    cs: { dob: 'Datum narození', no: 'Číslo', nat: 'Národnost', occ: 'Povolání', pob: 'Místo narození', poi: 'Místo vydání', doi: 'Datum vydání', exp: 'Platnost' },
  };
  const dict = replacements[lang] || replacements.en;
  const text = String(label || '').trim();
  return dict[text.toLowerCase()] || text;
}

function labelFromPlaceholder(row, lang = 'en') {
  const raw = String(row?.placeholder || row?.label || '').replace(/[{}]/g, '').trim();
  const schemaLabelTr = String(row?.schemaLabelTr || '').replace(/[{}]/g, '').trim();
  const labels = {
    en: { ApplicationType: 'Application Type', 'Application Type': 'Application Type', appType: 'Application Type', firstName: 'First Name', FirstName: 'First Name', 'Ad (First Name)': 'First Name', lastName: 'Last Name', LastName: 'Last Name', 'Soyad (Last Name)': 'Last Name', email: 'Email', Email: 'Email', 'E-posta (Email)': 'Email', phone: 'Phone', Phone: 'Phone', 'Telefon (Phone)': 'Phone', BirthCountry: 'Birth Country', countryOfBirth: 'Birth Country', BirthCountryCode: 'Birth Country Code', dateOfBirth: 'Birth Date', BirthDate: 'Birth Date', birthName: 'Birth Name', BirthName: 'Birth Name', placeOfBirth: 'Birth Place', BirthPlace: 'Birth Place', citizenship: 'Citizenship', Citizenship: 'Citizenship', gender: 'Gender', Gender: 'Gender', maritalStatus: 'Marital Status', MaritalStatus: 'Marital Status', nationality: 'Nationality', Nationality: 'Nationality', passportNumber: 'Passport No', PassportNumber: 'Passport No', dateOfIssue: 'Date of Issue', DateOfIssue: 'Date of Issue', expirationDate: 'Expiration Date', ExpirationDate: 'Expiration Date', nationalId: 'National ID', NationalId: 'National ID', employerName: 'Employer Name', EmployerName: 'Employer Name', companyName: 'Company', CompanyName: 'Company', cpcomanyname: 'Company Name', contactemployee_job_position: 'Job Position', position: 'Position', Position: 'Position', occupation: 'Occupation', Occupation: 'Occupation', address: 'Address', Address: 'Address', city: 'City', City: 'City', country: 'Country', Country: 'Country', postalCode: 'Postal Code', PostalCode: 'Postal Code', 'Başvuru Tipi': 'Application Type', 'Ad': 'First Name', 'Soyad': 'Last Name', 'E-posta': 'Email', 'Telefon': 'Phone', 'Cinsiyet': 'Gender', 'Doğum Yeri': 'Birth Place', 'Doğum Ülkesi': 'Birth Country', 'Doğum Tarihi': 'Birth Date', 'Medeni Durum': 'Marital Status', 'Şirket Adı': 'Company Name', 'İş Pozisyonu': 'Job Position' },
    tr: { ApplicationType: 'Başvuru Tipi', 'Application Type': 'Başvuru Tipi', appType: 'Başvuru Tipi', firstName: 'Ad', FirstName: 'Ad', 'Ad (First Name)': 'Ad', lastName: 'Soyad', LastName: 'Soyad', 'Soyad (Last Name)': 'Soyad', email: 'E-posta', Email: 'E-posta', 'E-posta (Email)': 'E-posta', phone: 'Telefon', Phone: 'Telefon', 'Telefon (Phone)': 'Telefon', BirthCountry: 'Doğum Ülkesi', countryOfBirth: 'Doğum Ülkesi', BirthCountryCode: 'Doğum Ülkesi Kodu', dateOfBirth: 'Doğum Tarihi', BirthDate: 'Doğum Tarihi', birthName: 'Doğum Adı', BirthName: 'Doğum Adı', placeOfBirth: 'Doğum Yeri', BirthPlace: 'Doğum Yeri', citizenship: 'Vatandaşlık', Citizenship: 'Vatandaşlık', gender: 'Cinsiyet', Gender: 'Cinsiyet', maritalStatus: 'Medeni Durum', MaritalStatus: 'Medeni Durum', nationality: 'Uyruk', Nationality: 'Uyruk', passportNumber: 'Pasaport No', PassportNumber: 'Pasaport No', dateOfIssue: 'Veriliş Tarihi', DateOfIssue: 'Veriliş Tarihi', expirationDate: 'Geçerlilik Tarihi', ExpirationDate: 'Geçerlilik Tarihi', nationalId: 'Ulusal Kimlik No', NationalId: 'Ulusal Kimlik No', employerName: 'İşveren Adı', EmployerName: 'İşveren Adı', companyName: 'Şirket', CompanyName: 'Şirket', cpcomanyname: 'Şirket Adı', contactemployee_job_position: 'İş Pozisyonu', position: 'Pozisyon', Position: 'Pozisyon', occupation: 'Meslek', Occupation: 'Meslek', address: 'Adres', Address: 'Adres', city: 'Şehir', City: 'Şehir', country: 'Ülke', Country: 'Ülke', postalCode: 'Posta Kodu', PostalCode: 'Posta Kodu', 'Passport Upload': 'Pasaport Yükleme', 'Highest Education Level': 'En Yüksek Eğitim Seviyesi' },
    cs: { ApplicationType: 'Typ žádosti', appType: 'Typ žádosti', firstName: 'Jméno', FirstName: 'Jméno', lastName: 'Příjmení', LastName: 'Příjmení', email: 'E-mail', Email: 'E-mail', phone: 'Telefon', Phone: 'Telefon', BirthCountry: 'Země narození', countryOfBirth: 'Země narození', BirthCountryCode: 'Kód země narození', dateOfBirth: 'Datum narození', BirthDate: 'Datum narození', birthName: 'Rodné jméno', BirthName: 'Rodné jméno', placeOfBirth: 'Místo narození', BirthPlace: 'Místo narození', citizenship: 'Občanství', Citizenship: 'Občanství', gender: 'Pohlaví', Gender: 'Pohlaví', maritalStatus: 'Rodinný stav', MaritalStatus: 'Rodinný stav', nationality: 'Národnost', Nationality: 'Národnost', passportNumber: 'Číslo pasu', PassportNumber: 'Číslo pasu', dateOfIssue: 'Datum vydání', DateOfIssue: 'Datum vydání', expirationDate: 'Datum platnosti', ExpirationDate: 'Datum platnosti', nationalId: 'Národní ID', NationalId: 'Národní ID', employerName: 'Zaměstnavatel', EmployerName: 'Zaměstnavatel', companyName: 'Společnost', CompanyName: 'Společnost', cpcomanyname: 'Název společnosti', contactemployee_job_position: 'Pracovní pozice', position: 'Pozice', Position: 'Pozice', occupation: 'Povolání', Occupation: 'Povolání', address: 'Adresa', Address: 'Adresa', city: 'Město', City: 'Město', country: 'Země', Country: 'Země', postalCode: 'PSČ', PostalCode: 'PSČ', 'Başvuru Tipi': 'Typ žádosti', 'Ad': 'Jméno', 'Soyad': 'Příjmení', 'E-posta': 'E-mail', 'Telefon': 'Telefon', 'Cinsiyet': 'Pohlaví', 'Doğum Yeri': 'Místo narození', 'Doğum Ülkesi': 'Země narození', 'Doğum Tarihi': 'Datum narození', 'Medeni Durum': 'Rodinný stav', 'Şirket Adı': 'Název společnosti', 'İş Pozisyonu': 'Pracovní pozice' },
  };
  const dict = labels[lang] || labels.en;
  const keys = [row?.formKey, raw, row?.fieldName, row?.key, row?.updateKey].filter(Boolean);
  for (const key of keys) if (dict[key]) return expandFieldLabelAbbreviations(dict[key], lang);
  const normalizeLabelKey = (value) => String(value || '')
    .replace(/^contact\./, '')
    .replace(/^business\./, '')
    .replace(/[{}]/g, '')
    .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
    .toLowerCase()
    .replace(/[^a-z0-9ğüşöçıİĞÜŞÖÇ]+/g, '_')
    .replace(/^_+|_+$/g, '');
  const fieldLabels = {
    en: {
      dob: 'Date of Birth', no: 'Number', nat: 'Nationality', occ: 'Occupation', pob: 'Place of Birth', poi: 'Place of Issue', doi: 'Date of Issue', exp: 'Expiry',
      arrival_date: 'Anticipated Arrival Date', arrivaldate: 'Anticipated Arrival Date',
      app_type: 'Application Type', apptype: 'Application Type', application_type: 'Application Type',
      num_children: 'Number of Children', numchildren: 'Number of Children', how_many_children_do_you_have: 'Number of Children',
      num_siblings: 'Number of Siblings', numsiblings: 'Number of Siblings', how_many_siblings_do_you_have: 'Number of Siblings',
      company_address_country: 'Company Address Country', company_address_country_country_list: 'Company Address Country',
      company_address_municipality: 'Company Address Municipality', company_address_district: 'Company Address District',
      company_address_street: 'Company Address Street', company_address_building_number: 'Company Address Building Number',
      company_address_postal_zip_code: 'Company Address Postal Zip Code', contactemployee_work_address: 'Work Address',
      contract_start_date: 'Contract Start Date', contractstart_date: 'Contract Start Date', contractstartdate: 'Contract Start Date',
      employee_contract_start_date: 'Contract Start Date', contract_end_date: 'Contract End Date', contractend_date: 'Contract End Date',
      contractenddate: 'Contract End Date', employee_contract_end_date: 'Contract End Date', place_of_work: 'Place of Work',
      emp_employer: 'Previous Employer', empemployer: 'Previous Employer', emp_position: 'Previous Position', empposition: 'Previous Position',
      emp_country: 'Previous Employment Country', empcountry: 'Previous Employment Country',
      emp_municipality: 'Previous Employment Municipality', empmunicipality: 'Previous Employment Municipality',
      emp_municipal_district: 'Previous Employment District', empmunicipaldistrict: 'Previous Employment District',
      emp_street: 'Previous Employment Street', empstreet: 'Previous Employment Street',
      emp_number: 'Previous Employment Number', empnumber: 'Previous Employment Number',
      emp_post_code: 'Previous Employment Postal Code', emppostcode: 'Previous Employment Postal Code',
    },
    tr: {
      dob: 'Doğum Tarihi', no: 'Numara', nat: 'Uyruk', occ: 'Meslek', pob: 'Doğum Yeri', poi: 'Verildiği Yer', doi: 'Verildiği Tarih', exp: 'Geçerlilik',
      arrival_date: 'Beklenen Varış Tarihi', arrivaldate: 'Beklenen Varış Tarihi',
      app_type: 'Başvuru Tipi', apptype: 'Başvuru Tipi', application_type: 'Başvuru Tipi',
      num_children: 'Çocuk Sayısı', numchildren: 'Çocuk Sayısı', how_many_children_do_you_have: 'Çocuk Sayısı',
      num_siblings: 'Kardeş Sayısı', numsiblings: 'Kardeş Sayısı', how_many_siblings_do_you_have: 'Kardeş Sayısı',
      company_address_country: 'Şirket Adresi - Ülke', company_address_country_country_list: 'Şirket Adresi - Ülke',
      company_address_municipality: 'Şirket Adresi - Belediye', company_address_district: 'Şirket Adresi - İlçe',
      company_address_street: 'Şirket Adresi - Sokak', company_address_building_number: 'Şirket Adresi - Bina No',
      company_address_postal_zip_code: 'Şirket Adresi - Posta Kodu', contactemployee_work_address: 'Çalışma Adresi',
      contract_start_date: 'Sözleşme Başlangıç Tarihi', contractstart_date: 'Sözleşme Başlangıç Tarihi', contractstartdate: 'Sözleşme Başlangıç Tarihi',
      employee_contract_start_date: 'Sözleşme Başlangıç Tarihi', contract_end_date: 'Sözleşme Bitiş Tarihi', contractend_date: 'Sözleşme Bitiş Tarihi',
      contractenddate: 'Sözleşme Bitiş Tarihi', employee_contract_end_date: 'Sözleşme Bitiş Tarihi', place_of_work: 'Çalışma Yeri',
      emp_employer: 'Son İşveren', empemployer: 'Son İşveren', emp_position: 'Son Pozisyon', empposition: 'Son Pozisyon',
      emp_country: 'Son İstihdam Ülkesi', empcountry: 'Son İstihdam Ülkesi',
      emp_municipality: 'Son İstihdam Belediyesi', empmunicipality: 'Son İstihdam Belediyesi',
      emp_municipal_district: 'Son İstihdam İlçesi', empmunicipaldistrict: 'Son İstihdam İlçesi',
      emp_street: 'Son İstihdam Sokağı', empstreet: 'Son İstihdam Sokağı',
      emp_number: 'Son İstihdam Kapı/Bina No', empnumber: 'Son İstihdam Kapı/Bina No',
      emp_post_code: 'Son İstihdam Posta Kodu', emppostcode: 'Son İstihdam Posta Kodu',
    },
    cs: {
      dob: 'Datum narození', no: 'Číslo', nat: 'Národnost', occ: 'Povolání', pob: 'Místo narození', poi: 'Místo vydání', doi: 'Datum vydání', exp: 'Platnost',
      arrival_date: 'Očekávané datum příjezdu', arrivaldate: 'Očekávané datum příjezdu',
      app_type: 'Typ žádosti', apptype: 'Typ žádosti', application_type: 'Typ žádosti',
      num_children: 'Počet dětí', numchildren: 'Počet dětí', how_many_children_do_you_have: 'Počet dětí',
      num_siblings: 'Počet sourozenců', numsiblings: 'Počet sourozenců', how_many_siblings_do_you_have: 'Počet sourozenců',
      company_address_country: 'Adresa společnosti - země', company_address_country_country_list: 'Adresa společnosti - země',
      company_address_municipality: 'Adresa společnosti - obec', company_address_district: 'Adresa společnosti - okres',
      company_address_street: 'Adresa společnosti - ulice', company_address_building_number: 'Adresa společnosti - číslo budovy',
      company_address_postal_zip_code: 'Adresa společnosti - PSČ', contactemployee_work_address: 'Adresa pracoviště',
      contract_start_date: 'Datum začátku smlouvy', contractstart_date: 'Datum začátku smlouvy', contractstartdate: 'Datum začátku smlouvy',
      employee_contract_start_date: 'Datum začátku smlouvy', contract_end_date: 'Datum konce smlouvy', contractend_date: 'Datum konce smlouvy',
      contractenddate: 'Datum konce smlouvy', employee_contract_end_date: 'Datum konce smlouvy', place_of_work: 'Místo výkonu práce',
      emp_employer: 'Poslední zaměstnavatel', empemployer: 'Poslední zaměstnavatel', emp_position: 'Poslední pozice', empposition: 'Poslední pozice',
      emp_country: 'Země posledního zaměstnání', empcountry: 'Země posledního zaměstnání',
      emp_municipality: 'Obec posledního zaměstnání', empmunicipality: 'Obec posledního zaměstnání',
      emp_municipal_district: 'Okres posledního zaměstnání', empmunicipaldistrict: 'Okres posledního zaměstnání',
      emp_street: 'Ulice posledního zaměstnání', empstreet: 'Ulice posledního zaměstnání',
      emp_number: 'Číslo posledního zaměstnání', empnumber: 'Číslo posledního zaměstnání',
      emp_post_code: 'PSČ posledního zaměstnání', emppostcode: 'PSČ posledního zaměstnání',
    },
  };
  const fieldDict = fieldLabels[lang] || fieldLabels.en;
  const normalizedKeys = keys.map(normalizeLabelKey);
  for (const key of normalizedKeys) if (fieldDict[key]) return expandFieldLabelAbbreviations(fieldDict[key], lang);
  const abbreviatedKey = normalizedKeys
    .map((key) => key.match(/(?:^|_)(dob|nat|occ|pob|poi|doi|exp)$/)?.[1])
    .find(Boolean);
  if (abbreviatedKey && fieldDict[abbreviatedKey]) return fieldDict[abbreviatedKey];
  if (lang === 'tr' && schemaLabelTr) return expandFieldLabelAbbreviations(schemaLabelTr, lang);
  if (lang === 'tr' && raw) return expandFieldLabelAbbreviations(raw, lang);
  if (!raw) return row?.fieldName || (lang === 'tr' ? 'Alan' : lang === 'cs' ? 'Pole' : 'Field');
  const base = String(row?.formKey || raw);
  return expandFieldLabelAbbreviations(base.replace(/^contact\./, '').replace(/^business\./, '').replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ').trim().replace(/\b\w/g, (s) => s.toUpperCase()), lang);
}

function upperUiLabel(value, lang = 'en') {
  const text = String(value || '');
  if (lang === 'tr') return text.toLocaleUpperCase('tr-TR');
  if (lang === 'cs') return text.toLocaleUpperCase('cs-CZ');
  return text.toUpperCase();
}

const APPLICATION_TYPE_OPTIONS = [
  { value: 'for_a_permit', labels: { en: 'For a Permit', tr: 'İzin Başvurusu', cs: 'Nová žádost' } },
  { value: 'for_a_renewal', labels: { en: 'For a Renewal', tr: 'Yenileme Başvurusu', cs: 'Prodloužení' } },
  { value: 'change_of_employer_or_placement', labels: { en: 'Change of Employer or Placement', tr: 'İşveren veya İş Yeri Değişikliği', cs: 'Změna zaměstnavatele nebo pracovní pozice' } },
];

function isApplicationTypeField(row) {
  const key = `${row?.key || ''} ${row?.fieldName || ''} ${row?.formKey || ''}`.toLowerCase();
  return /apptype|applicationtype|application_type/.test(key);
}

function localizedFieldOptions(options, lang = 'en') {
  return (options || []).map((option) => {
    if (!option || typeof option !== 'object') return option;
    return {
      value: option.value,
      label: option.labels?.[lang] || option.labels?.en || option.label || option.value,
    };
  });
}

function stripSectionContext(label, sectionId, sectionTitle, lang = 'en') {
  const rawLabel = String(label || '').trim();
  if (!rawLabel) return rawLabel;
  const context = `${sectionId || ''} ${sectionTitle || ''}`.toLocaleLowerCase(lang === 'tr' ? 'tr-TR' : undefined);
  let prefixes = [];
  if (/czechia_address|çekya adresi|address in czechia|adresa v česku/.test(context)) {
    prefixes = ['Çekya', 'Cz', 'Czechia', 'Česko', 'Česká', 'Český'];
  } else if (/residence_abroad|ikamet adresi|residence address|adresa bydliště/.test(context)) {
    prefixes = ['İkamet', 'Residence', 'Bydliště', 'Res'];
  } else if (/shipping_address|kargo adresi|shipping address|delivery|doručovací/.test(context)) {
    prefixes = ['Kargo', 'Delivery', 'Shipping', 'Doručovací', 'Res'];
  } else if (/^company\b|şirket bilgileri|employer information|údaje zaměstnavatele/.test(context)) {
    prefixes = ['Şirket Adresi', 'Company Address', 'Employer Address', 'Adresa společnosti'];
  } else if (/previous_stay_in_czech_republic|önceki|previous|předchozí/.test(context)) {
    prefixes = ['Prev Stay', 'Previous Stay', 'Önceki Kalış', 'Předchozí pobyt'];
  } else if (/last_employment|son istihdam|last employment|poslední zaměstnání/.test(context)) {
    prefixes = ['Son İstihdam', 'Last Employment', 'Previous Employment', 'Poslední zaměstnání'];
  } else if (/spouse_|eş bilgileri|spouse|manžel/.test(context)) {
    prefixes = ['Eş', 'Spouse', 'Manžel', 'Manželka'];
  } else if (/family_information.*(?:child|children|sibling|father|mother|parent)|children|çocuk|cocuk|siblings|kardeş|kardes|parents|anne|baba|father|mother|otec|matka/.test(context)) {
    prefixes = [
      'Child 1', 'Child 2', 'Child 3', 'Child 4', 'Child1', 'Child2', 'Child3', 'Child4',
      'Sibling 1', 'Sibling 2', 'Sibling 3', 'Sibling 4', 'Sibling1', 'Sibling2', 'Sibling3', 'Sibling4',
      'Çocuk 1', 'Çocuk 2', 'Çocuk 3', 'Çocuk 4', 'Kardeş 1', 'Kardeş 2', 'Kardeş 3', 'Kardeş 4',
      'Anne', 'Baba', 'Mother', 'Father', 'Parent', 'Matka', 'Otec',
      'Residence', 'İkamet', 'Bydliště', 'Res',
    ];
  } else if (/passport_travel_document|pasaport|passport|cestovní pas/.test(context)) {
    prefixes = ['Pasaport', 'Passport', 'Cestovní pas', 'Pass'];
  }
  if (!prefixes.length) return expandFieldLabelAbbreviations(rawLabel, lang);
  const escaped = prefixes.map((prefix) => prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
  const prefixPattern = new RegExp(`^(?:${escaped})(?:\\s+(?:Adresi|Address))?\\s*(?:[-–—:/]\\s*)?`, 'i');
  const prefixOnlyPattern = new RegExp(`^(?:${escaped})\\s*(?:[-–—:/]\\s*)?`, 'i');
  let cleaned = rawLabel;
  let previous = '';
  while (cleaned && cleaned !== previous) {
    previous = cleaned;
    const contextual = cleaned.replace(prefixPattern, '').replace(/^[\s\-–—:/]+/, '').trim();
    cleaned = (contextual || cleaned.replace(prefixOnlyPattern, '')).replace(/^[\s\-–—:/]+/, '').trim();
  }
  cleaned = cleaned.replace(/[\s\-–—:/]+$/, '').trim();
  if (!cleaned) return rawLabel;
  const turkishContextNouns = { Adresi: 'Adres', Ülkesi: 'Ülke', Belediyesi: 'Belediye', İlçesi: 'İlçe', Sokağı: 'Sokak', Numarası: 'Numara' };
  return expandFieldLabelAbbreviations(turkishContextNouns[cleaned] || cleaned, lang);
}

function schemaSectionFallbackTitle(id, lang = 'en') {
  const titles = {
    contact_kisisel_iletisim: { en: 'Personal Contact', tr: 'Kişisel İletişim', cs: 'Osobní kontakt' },
    personal_information: { en: 'Personal Information', tr: 'Kişisel Bilgiler', cs: 'Osobní údaje' },
    company: { en: 'Employer Information', tr: 'Şirket Bilgileri', cs: 'Údaje zaměstnavatele' },
    last_employment: { en: 'Last Employment', tr: 'Son İstihdam', cs: 'Poslední zaměstnání' },
    residence_abroad: { en: 'Residence Address', tr: 'İkamet Adresi', cs: 'Adresa bydliště' },
    czechia_address: { en: 'Czechia Address', tr: 'Çekya Adresi', cs: 'Adresa v Česku' },
    shipping_address_shippingsameasczechia_no_ise_gosterilir: { en: 'Shipping Address', tr: 'Kargo Adresi', cs: 'Adresa pro zásilky' },
    passport_travel_document: { en: 'Passport / Travel Document', tr: 'Pasaport / Seyahat Belgesi', cs: 'Cestovní pas / Doklad' },
    previous_stay_in_czech_republic: { en: 'Previous Stay in Czechia', tr: 'Önceki Çekya Konaklaması', cs: 'Předchozí pobyt v Česku' },
    spouse_maritalstatus_married_ise: { en: 'Spouse Information', tr: 'Eş Bilgileri', cs: 'Údaje o manželovi/manželce' },
    children_1_4_dinamik: { en: 'Children', tr: 'Çocuk Bilgileri', cs: 'Děti' },
    parents: { en: 'Parents', tr: 'Anne / Baba Bilgileri', cs: 'Rodiče' },
    siblings_1_4_dinamik: { en: 'Siblings', tr: 'Kardeş Bilgileri', cs: 'Sourozenci' },
    consent: { en: 'Consents', tr: 'Onaylar', cs: 'Souhlasy' },
  };
  const entry = titles[id];
  return entry ? (entry[lang] || entry.en) : '';
}

function buildContactCardSections(contact, rows, formKey, lang = 'en', t = (key) => key, getCurrentAnswer) {
  const sectionLabels = {
    en: { contact: 'Personal Contact', personal: 'Personal Information', company: 'Employer Information', passport: 'Passport / Travel Document', family: 'Family Information', approvals: 'Approvals', fallback: 'Form Section' },
    tr: { contact: 'Kişisel İletişim', personal: 'Kişisel Bilgiler', company: 'İşveren Bilgileri', passport: 'Pasaport / Seyahat Belgesi', family: 'Aile Bilgileri', approvals: 'Onaylar', fallback: 'Form Bölümü' },
    cs: { contact: 'Osobní kontakt', personal: 'Osobní údaje', company: 'Údaje zaměstnavatele', passport: 'Pas / cestovní doklad', family: 'Rodinné údaje', approvals: 'Souhlasy', fallback: 'Sekce formuláře' },
  };
  const st = sectionLabels[lang] || sectionLabels.en;
  const mkRow = (label, value, key, sourceRow) => Object.assign({}, sourceRow || {}, { l: label, v: formatFieldValue(value), key: key || sourceRow?.key || sourceRow?.fieldName || label });
  const requiredLabel = (row, fallback) => {
    const candidates = [row?.updateKey, row?.fieldName, row?.key, row?.formKey]
      .filter(Boolean)
      .map((key) => String(key).replace(/^contact\./, '').toLowerCase());
    const entry = (window.YEYE_REQUIRED_FIELDS || []).find((item) => (
      candidates.includes(String(item.key).replace(/^contact\./, '').toLowerCase())
    ));
    return entry ? t('wizard.field.' + entry.key) : fallback;
  };
  const familyTitles = {
    en: { father: 'Father', mother: 'Mother', child: 'Child', sibling: 'Sibling' },
    tr: { father: 'Baba', mother: 'Anne', child: 'Çocuk', sibling: 'Kardeş' },
    cs: { father: 'Otec', mother: 'Matka', child: 'Dítě', sibling: 'Sourozenec' },
  }[lang] || {};
  const schemaRows = rows.filter((row) => row.schemaSource === 'v3');
  if (schemaRows.length) {
    const bucketFor = (row) => {
      const id = String(row.sectionId || '');
      const title = String(schemaSectionFallbackTitle(id, lang) || row.sectionTitle || row.section || st.fallback);
      const filters = {
        contact_kisisel_iletisim: 'contact_kisisel_iletisim',
        personal_information: 'personal_information',
        company: 'company',
        last_employment: 'last_employment',
        residence_abroad: 'addresses',
        czechia_address: 'addresses',
        shipping_address_shippingsameasczechia_no_ise_gosterilir: 'addresses',
        passport_travel_document: 'passport_travel_document',
        previous_stay_in_czech_republic: 'last_employment',
        spouse_maritalstatus_married_ise: 'family_information',
        children_1_4_dinamik: 'family_information',
        parents: 'family_information',
        siblings_1_4_dinamik: 'family_information',
        consent: 'approvals',
      };
      return { id, title, filterId: filters[id] || id };
    };
    const familyMemberIndex = (row, type) => {
      const raw = `${row?.label || row?.placeholder || ''} ${row?.fieldName || ''} ${row?.formKey || ''}`.toLowerCase();
      const pattern = type === 'child'
        ? /(?:çocuk|child)\s*([1-4])|child([1-4])/
        : /(?:kardeş|kardes|sibling)\s*([1-4])|sibling([1-4])/;
      const match = raw.match(pattern);
      const index = Number(match?.[1] || match?.[2] || 0);
      return index >= 1 && index <= 4 ? index : 0;
    };
    const familyCount = (type) => {
      const wanted = type === 'child' ? 'numChildren' : 'numSiblings';
      const row = schemaRows.find((item) => item.formKey === wanted);
      const currentValue = typeof getCurrentAnswer === 'function' ? getCurrentAnswer(wanted) : '';
      const value = currentValue !== '' && currentValue != null ? currentValue : (row ? row.value : '');
      if (/^(0|no|none|yok|hayır|hayir)$/i.test(String(value || '').trim())) return 0;
      const numeric = Number.parseInt(String(value || '').match(/\d+/)?.[0] || '', 10);
      return Number.isFinite(numeric) ? Math.max(0, Math.min(4, numeric)) : 4;
    };
    const sections = new Map();
    const addSectionRow = (section, row) => {
      if (!sections.has(section.id)) sections.set(section.id, Object.assign({}, section, { rows: [], seen: new Set() }));
      const target = sections.get(section.id);
      const key = row.updateKey || row.fieldName || row.key || row.label;
      if (target.seen.has(key)) return;
      target.seen.add(key);
      const label = stripSectionContext(labelFromPlaceholder(row, lang), section.id, section.title, lang);
      target.rows.push(mkRow(label, row.value, key, row));
    };
    schemaRows.forEach((row) => {
      const bucket = bucketFor(row);
      const sectionId = String(row.sectionId || '');
      const raw = `${sectionId} ${row.sectionTitle || row.section || ''}`.toLowerCase();
      if (/parents|anne|baba/.test(raw)) {
        const label = `${row.schemaLabelTr || ''} ${row.label || row.placeholder || ''} ${row.formKey || ''}`;
        const parent = /(?:^|\s)(?:Baba|Father|Otec|father)/i.test(label) ? 'father' : (/(?:^|\s)(?:Anne|Mother|Matka|mother)/i.test(label) ? 'mother' : '');
        if (parent) {
          addSectionRow({ id: `${bucket.filterId}:${sectionId}:${parent}`, title: familyTitles[parent], filterId: bucket.filterId }, row);
          return;
        }
      }
      if (/children|çocuk|cocuk/.test(raw)) {
        const index = familyMemberIndex(row, 'child');
        const count = familyCount('child');
        if (index && index <= count) {
          addSectionRow({ id: `${bucket.filterId}:${sectionId}:child${index}`, title: `${familyTitles.child} ${index}`, filterId: bucket.filterId }, row);
        }
        return;
      }
      if (/siblings|kardeş|kardes/.test(raw)) {
        const index = familyMemberIndex(row, 'sibling');
        const count = familyCount('sibling');
        if (index && index <= count) {
          addSectionRow({ id: `${bucket.filterId}:${sectionId}:sibling${index}`, title: `${familyTitles.sibling} ${index}`, filterId: bucket.filterId }, row);
        }
        return;
      }
      addSectionRow(bucket, row);
    });
    return Array.from(sections.values()).map((section) => ({
      id: section.id,
      title: section.title,
      filterId: section.filterId,
      rows: section.rows,
    })).filter((section) => section.rows.length);
  }
  const byPlaceholder = {};
  rows.forEach((row) => {
    const clean = String(row.placeholder || '').replace(/[{}]/g, '').trim();
    if (clean && !byPlaceholder[clean]) byPlaceholder[clean] = row;
  });
  const pickRow = (names, label, fallbackKeys) => {
    const row = (Array.isArray(names) ? names : [names]).map((name) => byPlaceholder[name]).find(Boolean);
    return mkRow(label, row?.value || contactFieldValue(contact, fallbackKeys || []), row?.fieldName || label);
  };
  const used = new Set();
  const use = (row) => {
    [row?.fieldName, row?.placeholder, String(row?.placeholder || '').replace(/[{}]/g, '').trim()].filter(Boolean).forEach((key) => used.add(key));
    return row;
  };

  const contactRows = [
    use(pickRow(['ApplicationType', 'Application Type', 'Başvuru Tipi'], labelFromPlaceholder({ formKey: 'appType' }, lang), ['empcapplicationtype', 'contact.empcapplicationtype'])),
    use(pickRow('FirstName', t('wizard.field.firstName'), ['firstName', 'first_name', 'contact.first_name'])),
    use(pickRow('LastName', t('wizard.field.lastName'), ['lastName', 'last_name', 'contact.last_name'])),
    use(pickRow('Email', t('wizard.field.email'), ['email', 'contact.email'])),
    use(pickRow('Phone', t('wizard.field.phone'), ['phone', 'contact.phone'])),
  ];

  const sectionMap = [
    { id: 'personal_information', title: st.personal, filterId: 'personal_information', test: (row) => row.section === 'A - Personal data' },
    { id: 'company', title: st.company, filterId: 'company', test: (row) => row.section === 'F - Employment / Company' },
    { id: 'addresses', title: lang === 'tr' ? 'Adres Bilgileri' : lang === 'cs' ? 'Adresní údaje' : 'Address Information', filterId: 'addresses', test: (row) => /^B |^C1 |^C2 /.test(row.section) },
    { id: 'passport_travel_document', title: st.passport, filterId: 'passport_travel_document', test: (row) => row.section === 'D - Passport / Permit' },
    { id: 'last_employment', title: lang === 'tr' ? 'Son İstihdam' : lang === 'cs' ? 'Poslední zaměstnání' : 'Last Employment', filterId: 'last_employment', test: (row) => row.section === 'E - Previous stay / Previous work' },
    { id: 'family_information', title: st.family, filterId: 'family_information', test: (row) => ['G - Spouse', 'H - Children', 'I - Parents', 'J - Siblings'].includes(row.section) },
    { id: 'approvals', title: st.approvals, filterId: 'approvals', test: (row) => row.section === 'K - Education / Occupation' },
  ];

  const sections = [{ id: 'contact_kisisel_iletisim', title: st.contact, filterId: 'contact_kisisel_iletisim', rows: contactRows }];
  sectionMap.forEach((def) => {
    const sectionRows = rows
      .filter((row) => ![row.fieldName, row.placeholder, String(row.placeholder || '').replace(/[{}]/g, '').trim()].some((key) => used.has(key)) && def.test(row))
      .map((row) => mkRow(requiredLabel(row, labelFromPlaceholder(row, lang)), row.value, row.fieldName));
    const dedupedRows = def.id === 'personal_information'
      ? sectionRows.filter((row) => !['Ad', 'Soyad', 'E-posta', 'Telefon', 'First Name', 'Last Name', 'Email', 'Phone', 'Jméno', 'Příjmení', 'E-mail'].includes(row.l))
      : sectionRows;
    if (dedupedRows.length) sections.push({ ...def, rows: dedupedRows });
  });
  return sections;
}

function applicationFieldKind(row, fallbackKind) {
  if (row?.kind) return row.kind;
  const key = `${row?.key || ''} ${row?.fieldName || ''} ${row?.formKey || ''} ${row?.fieldType || ''}`.toLowerCase();
  const label = `${row?.label || ''} ${row?.placeholder || ''} ${row?.l || ''}`.toLowerCase();
  const fieldType = String(row?.fieldType || '').toLowerCase();
  if (/(^|[.\s_])marketing_consent($|[\s_])|marketingconsent/.test(key)) return 'checkbox';
  if (/passport_place_of_issue|passplaceofissue|place_of_birth|placeofbirth|place_of_work|placeofwork/.test(key)) return 'text';
  if (/email/.test(key)) return 'email';
  if (/phone|telefon/.test(key)) return 'phone';
  if (/country_list|countryofbirth|country_of_birth|nationality|citizenship|residencecountry|previouscountry|deliverycountry|shippingcountry|company_address_country|employercountry|\bcountry\b|country$|country\s/.test(key)) return 'country';
  if (/apptype|applicationtype|application_type/.test(key)) return 'select';
  if (/gender/.test(key)) return 'select';
  if (/marital/.test(key)) return 'select';
  if (/how_many_children|how_many_siblings|numchildren|numsiblings/.test(key)) return 'select';
  if (/yesno|yes_no|has_|same/.test(key) || /var mı|mı\?|mi\?/.test(label)) return 'select';
  if (/date/.test(fieldType) && !/placeofbirth|place_of_birth|countryofbirth|country_of_birth/.test(key)) return 'date';
  if (/dateofbirth|date_of_birth|birthdate|passport_date_of_issue|passport_expiry_date|passdateofissue|passexpirydate|contractstartdate|contractenddate|start_date|end_date|expiry|expiration|arrival_date|arrivaldate|previousstayfrom|previousstayuntil|prevstayfrom|prevstayuntil|prev_stay_from|prev_stay_until|stayfrom|stayuntil/.test(key)) return 'date';
  return fallbackKind || 'text';
}

function applicationFieldOptions(row, fallbackOptions, lang) {
  const key = `${row?.key || ''} ${row?.fieldName || ''} ${row?.formKey || ''}`.toLowerCase();
  const label = `${row?.label || ''} ${row?.placeholder || ''} ${row?.l || ''}`.toLowerCase();
  if (isApplicationTypeField(row)) return localizedFieldOptions(APPLICATION_TYPE_OPTIONS, lang);
  if (Array.isArray(row?.options) && row.options.length) return localizedFieldOptions(row.options, lang);
  if (/how_many_children|how_many_siblings|numchildren|numsiblings/.test(key)) return ['0', '1', '2', '3', '4'];
  if (/gender/.test(key)) {
    if (lang === 'tr') return ['Kadın', 'Erkek', 'Diğer'];
    if (lang === 'cs') return ['Žena', 'Muž', 'Jiné'];
    return ['Female', 'Male', 'Other'];
  }
  if (/marital/.test(key)) {
    if (lang === 'tr') return ['Bekar', 'Evli', 'Boşanmış', 'Dul'];
    if (lang === 'cs') return ['Svobodný/á', 'Ženatý/Vdaná', 'Rozvedený/á', 'Vdovec/Vdova'];
    return ['Single', 'Married', 'Divorced', 'Widowed'];
  }
  if (/yesno|yes_no|has_|same/.test(key) || /var mı|mı\?|mi\?/.test(label)) return lang === 'tr' ? ['Evet', 'Hayır'] : ['Yes', 'No'];
  return fallbackOptions;
}

function consentTypeForRow(row) {
  const key = `${row?.key || ''} ${row?.fieldName || ''} ${row?.formKey || ''}`.toLowerCase();
  if (/(^|[.\s_])gdpr_consent($|[\s_])|gdprconsent/.test(key)) return 'gdpr';
  if (/(^|[.\s_])marketing_consent($|[\s_])|marketingconsent/.test(key)) return 'marketing';
  return '';
}

function confirmedConsentValue(value) {
  return value === true || ['yes', 'true', '1', 'on'].includes(String(value || '').trim().toLowerCase());
}

function ContactRow({ row, editing, editValue, onEditChange, error }) {
  const { t, lang } = useT();
  const filled = isFilledContactValue(row.v);
  const consentType = consentTypeForRow(row);
  const currentConsentValue = editValue != null ? editValue : row.v;
  const consentConfirmed = confirmedConsentValue(currentConsentValue);
  const [consentOpen, setConsentOpen] = useState(false);
  const [consentRead, setConsentRead] = useState(false);
  const rowLabel = consentType === 'gdpr'
    ? t('form.gdprConsent')
    : consentType === 'marketing' ? t('form.marketingConsent') : row.l;
  if (consentType) {
    const openConsent = () => {
      setConsentRead(consentConfirmed);
      setConsentOpen(true);
    };
    const closeConsent = () => setConsentOpen(false);
    return (
      <>
        <div className="consent-row" data-profile-field={row.key}>
          <div className="consent-copy">
            <span style={{ fontSize: 12.5, fontWeight: 650, color: 'var(--ink-600)' }}>{rowLabel}</span>
            <Badge tone={consentConfirmed ? 'ok' : 'neut'}>
              {consentConfirmed && <Icon name="check" size={13} />}
              {t(consentConfirmed ? 'consent.confirmed' : 'consent.notConfirmed')}
            </Badge>
          </div>
          <Btn type="button" variant="ghost" size="sm" icon="book-open" onClick={openConsent}>{t('consent.readAndConfirm')}</Btn>
        </div>
        {consentOpen && (
          <Modal
            title={t(`consent.${consentType}.title`)}
            icon="shield-check"
            onClose={closeConsent}
            footer={(
              <>
                <label className="consent-check">
                  <input type="checkbox" checked={consentRead} onChange={(event) => setConsentRead(event.target.checked)} />
                  <span>{t('consent.iHaveRead')}</span>
                </label>
                <Btn type="button" variant="ghost" onClick={closeConsent}>{t('consent.cancel')}</Btn>
                <Btn type="button" variant="primary" icon="check" disabled={!consentRead} onClick={() => {
                  if (onEditChange) onEditChange(row.key, 'yes');
                  closeConsent();
                }}>{t('consent.confirm')}</Btn>
              </>
            )}
          >
            <div className="consent-body">{t(`consent.${consentType}.body`)}</div>
          </Modal>
        )}
      </>
    );
  }
  if (editing) {
    const kind = applicationFieldKind(row, (window.YEYE_PROFILE && window.YEYE_PROFILE.kindFor(row.key)) || 'text');
    const options = applicationFieldOptions(row, (window.YEYE_PROFILE && window.YEYE_PROFILE.optionsFor(row.key)) || null, lang);
    const currentValue = editValue != null ? editValue : (row.v || '');
    if (isApplicationTypeField(row) && filled) {
      const selected = (options || []).find((option) => String(option?.value ?? option) === String(currentValue));
      const displayValue = selected && typeof selected === 'object' ? selected.label : currentValue;
      return (
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, padding: '9px 10px', borderBottom: '1px solid var(--line-2)' }}>
          <span style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--ink-500)', flex: '0 0 40%' }}>{row.l}</span>
          <span className="flex ac gap-7" style={{ fontSize: 13.5, color: 'var(--ink-900)', fontWeight: 650, textAlign: 'right' }}>
            <Icon name="lock-keyhole" size={13} />{displayValue}
          </span>
        </div>
      );
    }
    const commonStyle = { flex: '1 1 auto', maxWidth: '55%', textAlign: 'right' };
    const inputId = `profile-field-${String(row.key || '').replace(/[^a-z0-9_-]/gi, '-')}`;
    const widget = window.renderProfileFieldWidget({
      kind,
      value: currentValue,
      onChange: (value) => onEditChange && onEditChange(row.key, value),
      options,
      label: rowLabel,
      lang,
      placeholder: filled ? '' : t('form.missing'),
      style: commonStyle,
      id: inputId,
      invalid: !!error,
    });
    return (
      <div data-profile-field={row.key} style={{
        display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, flexWrap: 'wrap',
        padding: '9px 10px',
        borderBottom: '1px solid var(--line-2)',
        borderRadius: error ? 8 : 0,
        background: error ? 'var(--bad-bg)' : 'transparent',
      }}>
        <label htmlFor={inputId} style={{ fontSize: 12.5, fontWeight: 600, color: error ? 'var(--bad)' : 'var(--ink-500)', flex: '0 0 40%' }}>{rowLabel}</label>
        {widget}
        {error && <div style={{ flex: '1 0 100%', paddingLeft: '40%', color: 'var(--bad)', fontSize: 11.5, fontWeight: 650 }}>{error}</div>}
      </div>
    );
  }
  return (
    <div style={{
      display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 16,
      margin: filled ? 0 : '6px 0', padding: filled ? '10px 0' : '9px 10px',
      borderBottom: filled ? '1px solid var(--line-2)' : 'none',
      border: filled ? 'none' : '1px solid var(--bad-line)',
      borderRadius: filled ? 0 : 10, background: filled ? 'transparent' : 'var(--bad-bg)'
    }}>
      <span style={{ fontSize: 12.5, fontWeight: filled ? 500 : 700, color: filled ? 'var(--ink-500)' : 'var(--bad)' }}>{rowLabel}</span>
      <span style={{ fontSize: 13.5, color: filled ? 'var(--ink-900)' : 'var(--bad)', fontWeight: 650, textAlign: 'right', wordBreak: 'break-word' }}>
        {filled ? row.v : t('form.missing')}
      </span>
    </div>
  );
}

function ApplicationFieldsCard({ ct, contact, fieldMappings }) {
  const { t } = useT();
  const [showAll, setShowAll] = useState(false);
  const formKey = ct && (ct.key === 'blue_card' || ct.key === 'employee_card') ? ct.key : '';
  if (!formKey || !window.YEYE_FIELD_MAPPING) return null;
  const rows = window.YEYE_FIELD_MAPPING.forForm(fieldMappings || [], formKey, contact || {});
  const filled = rows.filter((row) => row.value);
  const visibleRows = showAll ? rows : (filled.length ? filled : rows).slice(0, 24);
  const sections = visibleRows.reduce((acc, row) => {
    (acc[row.section] = acc[row.section] || []).push(row);
    return acc;
  }, {});
  const copy = {
    empty: contact ? t('form.empty') : t('form.notConnected'),
    mapped: t('form.mapped'),
    submitted: t('form.submitted'),
    showAll: t('form.showAll'),
    showFilled: t('form.showFilled'),
    missing: t('form.waiting'),
  };

  return (
    <Card>
      <CardHead icon="clipboard-list" title={t('form.applicationData')} sub={`${rows.length} ${copy.mapped} · ${filled.length} ${copy.submitted}`} />
      <div className="card-bd" style={{ paddingTop: 0 }}>
        {!rows.length ? (
          <div className="muted" style={{ padding: '4px 0 14px' }}>{copy.empty}</div>
        ) : (
          <>
            <div className="flex jb ac wrap gap-10 mb-14">
              <div className="muted" style={{ fontSize: 12.5 }}>Blue Card / Employee Card fields are read from the GHL mapping library.</div>
              <Btn variant="ghost" size="sm" icon={showAll ? 'eye' : 'list'} onClick={() => setShowAll((v) => !v)}>
                {showAll ? copy.showFilled : copy.showAll}
              </Btn>
            </div>
            <div className="flex col gap-16">
              {Object.keys(sections).map((section) => (
                <div key={section}>
                  <div className="eyebrow mb-8">{section}</div>
                  <div className="grid g-12" style={{ gap: 10 }}>
                    {sections[section].map((row) => (
                      <div className="col-4" key={`${row.fieldName}-${row.placeholder}`}>
                        <div style={{ border: '1px solid var(--line-2)', borderRadius: 'var(--r-sm)', padding: 12, minHeight: 76, background: row.value ? 'var(--surface)' : 'var(--surface-2)' }}>
                          <div className="dim" style={{ fontSize: 11.5, marginBottom: 6, lineHeight: 1.35 }}>{row.label}</div>
                          <div className={row.value ? 'strong' : 'muted'} style={{ fontSize: 13, lineHeight: 1.35, wordBreak: 'break-word' }}>
                            {formatFieldValue(row.value) || copy.missing}
                          </div>
                        </div>
                      </div>
                    ))}
                  </div>
                </div>
              ))}
            </div>
          </>
        )}
      </div>
    </Card>
  );
}

const INTERNAL_APPLICATION_FORMS = [
  { key: 'blue_card', icon: 'badge-check', label: { en: 'Blue Card Application Form', tr: 'Blue Card Application Form', cs: 'Blue Card Application Form' } },
  { key: 'employee_card', icon: 'id-card', label: { en: 'Employee Card Application Form', tr: 'Employee Card Application Form', cs: 'Employee Card Application Form' } },
  { key: 'employee_onboarding', icon: 'clipboard-check', label: { en: 'Employee Onboarding', tr: 'Employee Onboarding', cs: 'Employee Onboarding' } },
];

function applicationFormLabel(key, lang) {
  const form = INTERNAL_APPLICATION_FORMS.find((item) => item.key === key);
  return form ? (form.label[lang] || form.label.en) : key;
}

function realGhlFieldId(row, activeFormKey) {
  const candidates = [
    activeFormKey === 'blue_card' ? row?.blueId : '',
    activeFormKey === 'employee_card' ? row?.employeeId : '',
    activeFormKey === 'employee_onboarding' ? row?.onboardingId : '',
    row?.sourceCrmId, row?.fieldId, row?.id, row?.updateKey,
  ];
  return candidates.find((id) => {
    const v = String(id || '').trim();
    return v && v !== 'native' && v !== 'existing' && !/^contact\./.test(v) && !/^business\./.test(v);
  }) || '';
}

function consentGhlFieldId(contact, row) {
  const rowKey = `${row?.fieldName || ''} ${row?.key || ''} ${row?.formKey || ''}`.toLowerCase();
  const consentType = consentTypeForRow(row);
  if (!consentType) return '';
  const raw = (contact && contact.raw) || contact || {};
  const fields = (contact && contact.customFields) || raw.customFields || [];
  if (!Array.isArray(fields)) return '';
  const match = fields.find((field) => {
    const key = String(field && (field.key || field.fieldKey || field.name) || '').replace(/^contact\./, '').toLowerCase();
    return key === consentType + '_consent';
  });
  return match ? String(match.id || match.fieldId || match.customFieldId || '') : '';
}

function applicationBasicKey(row) {
  const raw = String(row?.fieldName || row?.updateKey || row?.key || '').replace(/^contact\./, '').replace(/^business\./, '');
  const map = {
    first_name: 'firstName',
    last_name: 'lastName',
    date_of_birth: 'dateOfBirth',
    email: 'email',
    phone: 'phone',
    country: 'country',
    address1: 'address1',
    city: 'city',
    state: 'state',
    postal_code: 'postalCode',
    postalCode: 'postalCode',
    company_name: 'companyName',
  };
  return map[raw] || (['firstName', 'lastName', 'email', 'phone', 'address1', 'city', 'state', 'country', 'postalCode', 'companyName', 'website', 'dateOfBirth'].includes(raw) ? raw : '');
}

function normalizedAnswer(value) {
  return String(value || '').trim().toLowerCase()
    .normalize('NFD').replace(/[\u0300-\u036f]/g, '');
}

function visibleApplicationSection(section, getAnswer) {
  const id = String(section?.id || '').toLowerCase();
  if (id.includes('spouse')) {
    const marital = normalizedAnswer(getAnswer('maritalStatus') || getAnswer('contact.marital_status'));
    return /married|evli|zenaty|vdana|ženat|vdan/.test(marital);
  }
  if (id.includes('children')) {
    const count = Number.parseInt(getAnswer('numChildren') || getAnswer('contact.how_many_children_do_you_have') || '0', 10);
    return count > 0 || section.rows.some((row) => /how_many_children|numchildren|has_children|child_tax|dependents/i.test(`${row.key} ${row.formKey}`));
  }
  if (id.includes('siblings')) {
    const count = Number.parseInt(getAnswer('numSiblings') || getAnswer('contact.how_many_siblings_do_you_have') || '0', 10);
    return count > 0;
  }
  return true;
}

function visibleApplicationRows(section, getAnswer) {
  const childCount = Number.parseInt(getAnswer('numChildren') || getAnswer('contact.how_many_children_do_you_have') || '0', 10);
  const siblingCount = Number.parseInt(getAnswer('numSiblings') || getAnswer('contact.how_many_siblings_do_you_have') || '0', 10);
  return section.rows.filter((row) => {
    const text = `${row.key || ''} ${row.fieldName || ''} ${row.formKey || ''}`.toLowerCase();
    const childMatch = text.match(/child(?:ren)?[_\s-]*(\d)|empcchild(\d)/);
    if (childMatch) return Number(childMatch[1] || childMatch[2]) <= Math.max(0, childCount);
    const siblingMatch = text.match(/sibling[_\s-]*(\d)|empcsibling(\d)/);
    if (siblingMatch) return Number(siblingMatch[1] || siblingMatch[2]) <= Math.max(0, siblingCount);
    return true;
  });
}

function rowLookupValue(rows, edits, keys) {
  const wanted = keys.map((key) => String(key || '').toLowerCase());
  const row = rows.find((item) => wanted.some((key) => [item.formKey, item.key, item.fieldName, item.updateKey]
    .map((v) => String(v || '').toLowerCase()).includes(key)));
  if (!row) return '';
  const editKey = [row.key, row.updateKey, row.fieldName, row.formKey].find((key) => Object.prototype.hasOwnProperty.call(edits, key));
  return editKey ? edits[editKey] : (row.value || row.v || '');
}

function familyMemberForSection(section) {
  const id = String(section?.id || '').toLowerCase();
  if (id.includes('spouse_maritalstatus_married_ise')) return { key: 'spouse', type: 'spouse' };
  if (/:father$/.test(id)) return { key: 'father', type: 'father' };
  if (/:mother$/.test(id)) return { key: 'mother', type: 'mother' };
  const child = id.match(/:child([1-4])$/);
  if (child) return { key: `child${child[1]}`, type: 'child', index: Number(child[1]) };
  const sibling = id.match(/:sibling([1-4])$/);
  if (sibling) return { key: `sibling${sibling[1]}`, type: 'sibling', index: Number(sibling[1]) };
  return null;
}

function isFamilyAddressRow(row) {
  const key = [row?.formKey, row?.fieldName]
    .filter(Boolean)
    .join(' ')
    .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
    .toLowerCase();
  if (/nationality|citizenship|birth_country|country_of_birth/.test(key)) return false;
  return /res|address|country|municipality|district|street|building|postal|zip/.test(key);
}

function partitionFamilyRows(rows) {
  return rows.reduce((groups, row) => {
    groups[isFamilyAddressRow(row) ? 1 : 0].push(row);
    return groups;
  }, [[], []]);
}

function familyResidenceFormKeys(member) {
  if (!member) return [];
  if (member.type === 'child' || member.type === 'sibling') return [`${member.key}ResAddress`];
  const prefix = member.type === 'spouse' ? 'spouse' : member.type;
  return [
    `${prefix}ResCountry`, `${prefix}ResMunicipality`, `${prefix}ResDistrict`,
    `${prefix}ResStreet`, `${prefix}ResNumber`, `${prefix}ResPostCode`,
  ];
}

function applicationRowAnswerKey(row) {
  return row?.updateKey || row?.fieldName || row?.key || row?.formKey || '';
}

function currentResidenceParts(rows, edits) {
  return {
    country: rowLookupValue(rows, edits, ['resCountry', 'contact.country_residence_country_list']),
    municipality: rowLookupValue(rows, edits, ['resMunicipality', 'contact.municipality_in_residence']),
    district: rowLookupValue(rows, edits, ['resDistrict', 'contact.district']),
    street: rowLookupValue(rows, edits, ['resStreetNumber', 'contact.street_number']),
    number: rowLookupValue(rows, edits, ['resBuildingNumber', 'contact.building_no']),
    postCode: rowLookupValue(rows, edits, ['resPostalCode', 'contact.zip_code']),
  };
}

function combinedResidenceAddress(parts) {
  return [parts.street, parts.number, parts.district, parts.municipality, parts.postCode, parts.country]
    .map((value) => String(value || '').trim()).filter(Boolean).join(', ');
}

function parentResidenceParts(rows, edits, parent) {
  const prefix = parent === 'mother' ? 'mother' : 'father';
  return {
    country: rowLookupValue(rows, edits, [`${prefix}ResCountry`]),
    municipality: rowLookupValue(rows, edits, [`${prefix}ResMunicipality`]),
    district: rowLookupValue(rows, edits, [`${prefix}ResDistrict`]),
    street: rowLookupValue(rows, edits, [`${prefix}ResStreet`]),
    number: rowLookupValue(rows, edits, [`${prefix}ResNumber`]),
    postCode: rowLookupValue(rows, edits, [`${prefix}ResPostCode`]),
  };
}

function residencePartsHaveValue(parts) {
  return Object.values(parts).some((value) => String(value || '').trim());
}

function residencePatchFromParts(rows, member, parts) {
  const targetValues = member?.type === 'child' || member?.type === 'sibling'
    ? [combinedResidenceAddress(parts)]
    : [parts.country, parts.municipality, parts.district, parts.street, parts.number, parts.postCode];
  const patch = {};
  familyResidenceFormKeys(member).forEach((formKey, index) => {
    const row = rows.find((item) => item.formKey === formKey);
    const answerKey = applicationRowAnswerKey(row);
    if (answerKey) patch[answerKey] = targetValues[index] || '';
  });
  return patch;
}

function familyResidencePatch(rows, edits, member) {
  return residencePatchFromParts(rows, member, currentResidenceParts(rows, edits));
}

function parentResidencePatch(rows, edits, member) {
  const fatherParts = parentResidenceParts(rows, edits, 'father');
  const parts = residencePartsHaveValue(fatherParts)
    ? fatherParts
    : parentResidenceParts(rows, edits, 'mother');
  return residencePatchFromParts(rows, member, parts);
}

function residencePatchMatches(rows, edits, patch) {
  const entries = Object.entries(patch);
  if (!entries.length || !entries.some(([, value]) => String(value || '').trim())) return false;
  return entries.every(([answerKey, sourceValue]) => {
    const row = rows.find((item) => applicationRowAnswerKey(item) === answerKey);
    if (!row) return false;
    const targetValue = rowLookupValue(rows, edits, [row.formKey, row.key, row.fieldName, row.updateKey].filter(Boolean));
    return String(targetValue || '').trim() === String(sourceValue || '').trim();
  });
}

function familyResidenceMatches(rows, edits, member) {
  return residencePatchMatches(rows, edits, familyResidencePatch(rows, edits, member));
}

function siblingResidenceChoice(rows, edits, member) {
  if (familyResidenceMatches(rows, edits, member)) return 'mine';
  const matchesParent = ['father', 'mother'].some((parent) => residencePatchMatches(
    rows,
    edits,
    residencePatchFromParts(rows, member, parentResidenceParts(rows, edits, parent)),
  ));
  return matchesParent ? 'parents' : 'different';
}

function yesNoState(value) {
  const answer = normalizedAnswer(value);
  if (/^(yes|evet|ano|true|1)$/.test(answer)) return 'yes';
  if (/^(no|hayir|hayır|ne|false|0)$/.test(answer)) return 'no';
  return '';
}

function deliveryAddressPatch(rows, edits, source) {
  const sources = source === 'czechia'
    ? {
      country: ['czCountry', 'contact.country_in_czechia_country_list'],
      municipality: ['czMunicipality', 'contact.municipality_in_czechia'],
      district: ['czDistrict', 'contact.district_in_czechia'],
      street: ['czStreetNumber', 'contact.street_number_in_czechia'],
      building: ['czBuildingNumber', 'contact.building_number_in_czechia'],
      postCode: ['czPostalCode', 'contact.postalzip_code_in_czechia'],
    }
    : {
      country: ['resCountry', 'contact.country_residence_country_list'],
      municipality: ['resMunicipality', 'contact.municipality_in_residence'],
      district: ['resDistrict', 'contact.district'],
      street: ['resStreetNumber', 'contact.street_number'],
      building: ['resBuildingNumber', 'contact.building_no'],
      postCode: ['resPostalCode', 'contact.zip_code'],
    };
  const targets = {
    country: ['delCountry', 'contact.delivery_country_country_list'],
    municipality: ['delMunicipality', 'contact.delivery_municipality'],
    district: ['delDistrict', 'contact.delivery_municipal_district'],
    street: ['delStreet', 'contact.delivery_street'],
    building: ['delBuildingNumber', 'contact.delivery_building_number'],
    postCode: ['delPostCode', 'contact.delivery_post_code'],
  };
  const patch = {};
  Object.keys(targets).forEach((part) => {
    const target = rows.find((row) => targets[part].some((key) => [row.formKey, row.key, row.fieldName, row.updateKey]
      .map((candidate) => String(candidate || '').toLowerCase()).includes(String(key).toLowerCase())));
    const value = rowLookupValue(rows, edits, sources[part]);
    if (target && value) patch[target.key] = value;
  });
  return patch;
}

function addressDecisionRow(source, lang, currentValue) {
  const isResidence = source === 'residence';
  const labels = {
    tr: isResidence
      ? 'Kargo için bu ikamet adresini mi kullanıyorsunuz?'
      : 'Kargo için bu Çekya adresini mi kullanıyorsunuz?',
    en: isResidence
      ? 'Use this residence address for shipping?'
      : 'Use this Czechia address for shipping?',
    cs: isResidence
      ? 'Použít tuto adresu bydliště pro doručení?'
      : 'Použít tuto českou adresu pro doručení?',
  };
  return {
    virtual: true,
    noSave: true,
    kind: 'select',
    options: lang === 'tr' ? ['Evet', 'Hayır'] : lang === 'cs' ? ['Ano', 'Ne'] : ['Yes', 'No'],
    key: source === 'residence' ? '__deliverySameResidence' : '__deliverySameCzechia',
    l: labels[lang] || labels.en,
    v: currentValue || '',
  };
}

function serviceKeyToApplicationForm(key) {
  const v = String(key || '').toLowerCase();
  if (!v) return '';
  if (v.includes('blue_card') || v.includes('bluecard')) return 'blue_card';
  if (v.includes('employee_onboarding') || v.includes('onboarding') || v === 'eob') return 'employee_onboarding';
  if (v.includes('emp_card') || v.includes('employee_card')) return 'employee_card';
  return '';
}

function paidApplicationFormKeys(purchasedServices, contact) {
  const keys = new Set();
  (purchasedServices || []).forEach((item) => {
    const status = String(item?.status || 'active').toLowerCase();
    if (['cancelled', 'canceled', 'inactive', 'refunded'].includes(status)) return;
    const form = serviceKeyToApplicationForm(item?.k || item?.key || item?.serviceKey || item?.productKey);
    if (form) keys.add(form);
  });
  const tags = [
    ...((contact && contact.tags) || []),
    ...((contact && contact.raw && contact.raw.tags) || []),
  ].map((tag) => String(tag || '').toLowerCase());
  tags.forEach((tag) => {
    if (!/(payment|paid|purchased|received|svc:)/.test(tag)) return;
    const form = serviceKeyToApplicationForm(tag);
    if (form) keys.add(form);
  });
  return INTERNAL_APPLICATION_FORMS.map((form) => form.key).filter((key) => keys.has(key));
}

async function saveApplicationFormAnswers(contact, answers, rows, activeFormKey) {
  const contactId = contact && (contact.contactId || contact.id);
  if (!contactId) throw new Error('missing-contact');
  const basic = {};
  const customs = [];
  Object.entries(answers || {}).forEach(([key, rawValue]) => {
    if (String(key).startsWith('__')) return;
    const row = (rows || []).find((item) => [item.updateKey, item.fieldName, item.key, item.formKey].some((candidate) => String(candidate || '') === String(key)));
    if (!row) return;
    const value = rawValue == null ? '' : String(rawValue);
    const gid = realGhlFieldId(row, activeFormKey) || consentGhlFieldId(contact, row);
    const basicKey = applicationBasicKey(row);
    if (basicKey && !gid) basic[basicKey] = value;
    else if (gid) customs.push({ id: gid, ghlFieldId: gid, key: row.fieldName || row.key || key, value });
  });
  if (!window.YEYE_BACKEND || !window.YEYE_BACKEND.updateContactProfile) throw new Error('write-backend-not-configured');
  await window.YEYE_BACKEND.updateContactProfile({ contactId, basic, customFields: customs });
  return {
    updates: basic,
    customFieldUpdates: customs.map((field) => ({ id: field.id, key: field.key, value: field.value })),
  };
}

function auditFormSave(contact, formKey, answers, rows, sections, sourceScreen) {
  if (!window.YEYE_AUDIT) return;
  const contactId = contact && (contact.contactId || contact.id);
  const changes = Object.entries(answers || {}).flatMap(([key, newRaw]) => {
    if (String(key).startsWith('__')) return [];
    const row = (rows || []).find((item) => [item.updateKey, item.fieldName, item.key, item.formKey]
      .some((candidate) => String(candidate || '') === String(key)));
    if (!row) return [];
    const oldValue = row.v == null ? '' : String(row.v);
    const newValue = newRaw == null ? '' : String(newRaw);
    if (oldValue === newValue) return [];
    const section = (sections || []).find((item) => (item.rows || []).some((sectionRow) => (
      sectionRow === row || (row.key != null && sectionRow.key === row.key)
    )));
    return [{
      event: 'field.changed',
      contactId,
      payload: {
        fieldKey: row.fieldName || row.updateKey || row.key || key,
        oldValue: oldValue.slice(0, 60),
        newValue: newValue.slice(0, 60),
        section: (section && (section.id || section.title)) || null,
      },
    }];
  });
  if (!changes.length) return;
  const events = [{
    event: 'form.filled',
    contactId,
    payload: { formKey: formKey || null, fieldsChanged: changes.length, sourceScreen },
  }].concat(changes);
  if (window.YEYE_AUDIT.logBatch) window.YEYE_AUDIT.logBatch(events);
  else events.forEach((item) => window.YEYE_AUDIT.log(item.event, item.contactId, item.payload));
}

function ApplicationAutofillPanel({ section, rows, edits, onApply }) {
  const { lang } = useT();
  if (!section || !/address|residence|company|calisma|çalışma|izin/i.test(`${section.id} ${section.title}`)) return null;
  const copyAddress = (from, to) => {
    const pairs = [
      [['resCountry', 'contact.country_residence_country_list'], ['delCountry', 'contact.delivery_country_country_list']],
      [['resMunicipality', 'contact.municipality_in_residence'], ['delMunicipality', 'contact.delivery_municipality']],
      [['resDistrict', 'contact.district'], ['delDistrict', 'contact.delivery_municipal_district']],
      [['resStreetNumber', 'contact.street_number'], ['delStreet', 'contact.delivery_street']],
      [['resBuildingNumber', 'contact.building_no'], ['delBuildingNumber', 'contact.delivery_building_number']],
      [['resPostalCode', 'contact.zip_code'], ['delPostCode', 'contact.delivery_post_code']],
      [['czCountry', 'contact.country_in_czechia_country_list'], ['delCountry', 'contact.delivery_country_country_list']],
      [['czMunicipality', 'contact.municipality_in_czechia'], ['delMunicipality', 'contact.delivery_municipality']],
      [['czDistrict', 'contact.district_in_czechia'], ['delDistrict', 'contact.delivery_municipal_district']],
      [['czStreetNumber', 'contact.street_number_in_czechia'], ['delStreet', 'contact.delivery_street']],
      [['czBuildingNumber', 'contact.building_number_in_czechia'], ['delBuildingNumber', 'contact.delivery_building_number']],
      [['czPostalCode', 'contact.postalzip_code_in_czechia'], ['delPostCode', 'contact.delivery_post_code']],
    ].filter((pair) => pair[0][0].startsWith(from) && pair[1][0].startsWith(to));
    const patch = {};
    pairs.forEach(([sourceKeys, targetKeys]) => {
      const target = rows.find((row) => targetKeys.includes(row.formKey) || targetKeys.includes(row.key) || targetKeys.includes(row.fieldName));
      if (target) patch[target.key] = rowLookupValue(rows, edits, sourceKeys);
    });
    onApply(patch);
  };
  const copyWorkAddress = () => {
    const parts = [
      rowLookupValue(rows, edits, ['company_address_street', 'contact.company_address_street']),
      rowLookupValue(rows, edits, ['company_address_building_number', 'contact.company_address_building_number']),
      rowLookupValue(rows, edits, ['company_address_municipality', 'contact.company_address_municipality']),
      rowLookupValue(rows, edits, ['company_address_district', 'contact.company_address_district']),
      rowLookupValue(rows, edits, ['company_address_postal_zip_code', 'contact.company_address_postal_zip_code']),
      rowLookupValue(rows, edits, ['company_address_country', 'contact.company_address_country_country_list']),
    ].filter(Boolean).join(', ');
    const target = rows.find((row) => ['contactemployee_work_address', 'contact.contactemployee_work_address'].includes(row.formKey) || ['contactemployee_work_address', 'contact.contactemployee_work_address'].includes(row.key));
    if (target && parts) onApply({ [target.key]: parts });
  };
  const btnLabel = lang === 'tr' ? 'Aynıysa doldur' : 'Autofill if same';
  return (
    <div className="flex ac gap-8 wrap" style={{ padding: '12px 20px', borderBottom: '1px solid var(--line-2)', background: 'var(--surface-2)' }}>
      <Btn size="sm" variant="ghost" icon="copy" onClick={() => copyAddress('res', 'del')}>{lang === 'tr' ? 'Posta = İkamet' : 'Delivery = Residence'}</Btn>
      <Btn size="sm" variant="ghost" icon="copy" onClick={() => copyAddress('cz', 'del')}>{lang === 'tr' ? 'Posta = Çekya adresi' : 'Delivery = Czech address'}</Btn>
      <Btn size="sm" variant="ghost" icon="copy" onClick={copyWorkAddress}>{lang === 'tr' ? 'Çalışma = İşyeri' : 'Work = Company'}</Btn>
      <span className="muted" style={{ fontSize: 12 }}>{btnLabel}</span>
    </div>
  );
}

function ApplicationFormModal({ formKey, contact, rows, lang, t, onClose, onSaved, applyOptimisticUpdate }) {
  const [activeStep, setActiveStep] = React.useState(0);
  const [answers, setAnswers] = React.useState({});
  const [sameAddressChoices, setSameAddressChoices] = React.useState({});
  const [siblingAddressChoices, setSiblingAddressChoices] = React.useState({});
  const [saving, setSaving] = React.useState(false);
  const [error, setError] = React.useState('');
  const [validationAttempted, setValidationAttempted] = React.useState(false);
  const getValue = (row) => Object.prototype.hasOwnProperty.call(answers, row.key) ? answers[row.key] : row.v;
  const rawRows = rows || [];
  // Auto-fill spouse/children SURNAME with the applicant's own last name (they share a family name
  // by default). Only when the field is still empty and untouched; the user can override. Excludes
  // "previous surnames" (empcspouseprevioussurnames) since that is a maiden/former name field.
  React.useEffect(() => {
    const contactLast = (contact && (contact.lastName || contact.last_name))
      || contactFieldValue(contact || {}, ['lastName', 'last_name', 'contact.last_name']) || '';
    if (!contactLast) return;
    const seed = {};
    (rows || []).forEach((row) => {
      const id = String((row && (row.fieldName || row.formKey || row.key)) || '').toLowerCase();
      const isSurnameMirror = /empcspousesurname/.test(id) || /empcchild[1-4]surname/.test(id)
        || /^spousesurname$/.test(id) || /^child[1-4]surname$/.test(id);
      if (!isSurnameMirror) return;
      if (Object.prototype.hasOwnProperty.call(answers, row.key)) return;
      if (row.v != null && String(row.v).trim() !== '') return;
      seed[row.key] = contactLast;
    });
    if (Object.keys(seed).length) setAnswers((prev) => ({ ...prev, ...seed }));
  }, [rows, contact]);
  const getAnswer = (key) => rowLookupValue(rawRows, answers, [key]) || answers[key] || '';
  const sections = buildContactCardSections(contact || {}, rawRows, formKey, lang, t, getAnswer)
    .filter((section) => visibleApplicationSection(section, getAnswer))
    .map((section) => {
      const id = String(section.id || '').toLowerCase();
      const title = String(section.title || '').toLowerCase();
      let nextRows = visibleApplicationRows(section, getAnswer);
      if (/residence|ikamet|bydli/.test(`${id} ${title}`)) {
        nextRows = nextRows.concat(addressDecisionRow('residence', lang, answers.__deliverySameResidence));
      }
      if (/czechia|çekya|cekya|česku|cesku/.test(`${id} ${title}`) && yesNoState(answers.__deliverySameResidence) === 'no') {
        nextRows = nextRows.concat(addressDecisionRow('czechia', lang, answers.__deliverySameCzechia));
      }
      return { ...section, rows: nextRows };
    })
    .filter((section) => {
      const text = `${section.id || ''} ${section.title || ''}`.toLowerCase();
      if (!/delivery|shipping|kargo|doru/.test(text)) return true;
      return yesNoState(answers.__deliverySameResidence) === 'no' && yesNoState(answers.__deliverySameCzechia) === 'no';
    })
    .filter((section) => section.rows.length);
  const activeIndex = Math.min(activeStep, Math.max(sections.length - 1, 0));
  const activeSection = sections[activeIndex];
  const activeFamilyMember = familyMemberForSection(activeSection);
  const activeSiblingAddressChoice = activeFamilyMember?.type === 'sibling'
    ? (Object.prototype.hasOwnProperty.call(siblingAddressChoices, activeFamilyMember.key)
      ? siblingAddressChoices[activeFamilyMember.key]
      : siblingResidenceChoice(rawRows, answers, activeFamilyMember))
    : null;
  const activeSameAddress = activeFamilyMember && activeFamilyMember.type !== 'sibling'
    ? (Object.prototype.hasOwnProperty.call(sameAddressChoices, activeFamilyMember.key)
      ? sameAddressChoices[activeFamilyMember.key]
      : familyResidenceMatches(rawRows, answers, activeFamilyMember))
    : false;
  const hideActiveResidence = activeFamilyMember?.type === 'sibling'
    ? activeSiblingAddressChoice !== 'different'
    : activeSameAddress;
  const hiddenResidenceKeys = new Set(hideActiveResidence ? familyResidenceFormKeys(activeFamilyMember) : []);
  const activeRows = activeSection
    ? activeSection.rows.filter((row) => !hiddenResidenceKeys.has(row.formKey))
    : [];
  const visibleFormRows = Array.from(new Map(
    sections.flatMap((section) => section.rows).filter((row) => !row.virtual).map((row) => [row.key, row])
  ).values());
  const hasExplicitRequiredRows = rawRows.some((row) => row.required === true);
  const requiredRows = hasExplicitRequiredRows ? visibleFormRows.filter((row) => row.required === true) : visibleFormRows;
  const missingRequiredRows = requiredRows.filter((row) => !isFilledContactValue(getValue(row)));
  const missingRequiredKeys = new Set(missingRequiredRows.map((row) => row.key));
  const filledCount = rawRows.filter((row) => isFilledContactValue(getValue(row))).length;
  const progress = sections.length ? Math.round(((activeIndex + 1) / sections.length) * 100) : 0;
  const copy = {
    save: t('form.save') || 'Save',
    saving: t('c.saving') || 'Saving',
    cancel: t('c.cancel') || 'Cancel',
    back: lang === 'tr' ? 'Geri' : 'Back',
    next: lang === 'tr' ? 'İleri' : 'Next',
    step: lang === 'tr' ? 'Bölüm' : 'Section',
    required: t('form.requiredError'),
    fillRequired: t('form.fillRequired'),
  };
  React.useEffect(() => {
    if (activeStep >= sections.length) setActiveStep(Math.max(sections.length - 1, 0));
  }, [activeStep, sections.length]);
  React.useEffect(() => {
    if (activeFamilyMember?.type !== 'sibling') return;
    setSiblingAddressChoices((prev) => Object.prototype.hasOwnProperty.call(prev, activeFamilyMember.key)
      ? prev
      : { ...prev, [activeFamilyMember.key]: siblingResidenceChoice(rawRows, answers, activeFamilyMember) });
  }, [activeFamilyMember?.key]);
  const doSave = async (closeAfter = true) => {
    if (closeAfter && missingRequiredRows.length) {
      setValidationAttempted(true);
      const firstInvalid = missingRequiredRows[0];
      const invalidSectionIndex = sections.findIndex((section) => section.rows.some((row) => row.key === firstInvalid.key));
      if (invalidSectionIndex >= 0) setActiveStep(invalidSectionIndex);
      window.requestAnimationFrame(() => window.requestAnimationFrame(() => {
        const wrapper = Array.from(document.querySelectorAll('[data-profile-field]'))
          .find((node) => node.getAttribute('data-profile-field') === String(firstInvalid.key));
        if (!wrapper) return;
        wrapper.scrollIntoView({ behavior: 'smooth', block: 'center' });
        const input = wrapper.querySelector('input, select, textarea, button');
        if (input) input.focus({ preventScroll: true });
      }));
      return;
    }
    setSaving(true);
    setError('');
    try {
      const patch = await saveApplicationFormAnswers(contact, answers, rawRows, formKey);
      if (applyOptimisticUpdate) applyOptimisticUpdate(patch);
      auditFormSave(contact, formKey, answers, rawRows, sections, 'application_form_modal');
      // Hard-refresh the data after saving so the freshly persisted values show up: drop stale
      // client caches and re-pull the contact from GHL (source of truth). Keeps the form open on
      // a mid-step save; the parent re-renders with the fresh contact so field values reflect it.
      try { if (window.YEYE_CACHE) window.YEYE_CACHE.clearClientData(); } catch (_) {}
      try {
        if (window.YEYE_AUTH && window.YEYE_AUTH.refreshContactFromGhl) await window.YEYE_AUTH.refreshContactFromGhl();
      } catch (_) {}
      window.YEYE_TOAST && window.YEYE_TOAST(t('form.savedNext') || t('form.savedGhl') || 'Saved');
      if (onSaved) onSaved();
      if (closeAfter) onClose();
    } catch (err) {
      console.warn('Application form save failed', err);
      setError((err && err.message) || 'Save failed');
    } finally {
      setSaving(false);
    }
  };
  const updateAnswer = (row, value) => {
    setAnswers((prev) => {
      const base = { ...prev, [row.key]: value };
      if (row.key === '__deliverySameResidence' && yesNoState(value) === 'yes') {
        return { ...base, ...deliveryAddressPatch(rawRows, base, 'residence') };
      }
      if (row.key === '__deliverySameCzechia' && yesNoState(value) === 'yes') {
        return { ...base, ...deliveryAddressPatch(rawRows, base, 'czechia') };
      }
      return base;
    });
  };
  const updateSameAddress = (checked) => {
    if (!activeFamilyMember) return;
    setSameAddressChoices((prev) => ({ ...prev, [activeFamilyMember.key]: checked }));
    if (checked) {
      setAnswers((prev) => ({ ...prev, ...familyResidencePatch(rawRows, prev, activeFamilyMember) }));
    }
  };
  const updateSiblingAddress = (choice) => {
    if (activeFamilyMember?.type !== 'sibling') return;
    setSiblingAddressChoices((prev) => ({ ...prev, [activeFamilyMember.key]: choice }));
    if (choice === 'mine') {
      setAnswers((prev) => ({ ...prev, ...familyResidencePatch(rawRows, prev, activeFamilyMember) }));
    } else if (choice === 'parents') {
      setAnswers((prev) => ({ ...prev, ...parentResidencePatch(rawRows, prev, activeFamilyMember) }));
    }
  };
  const sameAddressLabel = activeFamilyMember?.type === 'spouse' && lang === 'tr'
    ? 'İkamet adresim eşimle aynı'
    : t('wizard.sameAddr.title');
  return (
    <Modal
      wide
      icon="clipboard-list"
      title={applicationFormLabel(formKey, lang)}
      sub={`${filledCount}/${rawRows.length} ${lang === 'tr' ? 'alan dolu' : 'fields filled'}`}
      onClose={onClose}
      footer={(
        <>
          <Btn variant="ghost" icon="x" disabled={saving} onClick={onClose}>{copy.cancel}</Btn>
          <Btn variant="ghost" icon="arrow-left" disabled={saving || activeIndex === 0} onClick={() => setActiveStep((v) => Math.max(0, v - 1))}>{copy.back}</Btn>
          {activeIndex < sections.length - 1 ? (
            <>
              {/* Save-and-stay: persist progress without leaving the form (available on every step). */}
              <Btn variant="soft" icon={saving ? 'loader-circle' : 'check'} disabled={saving || Object.keys(answers).length === 0} onClick={() => doSave(false)}>
                {saving ? copy.saving : copy.save}
              </Btn>
              <Btn variant="primary" iconR="arrow-right" disabled={saving || sections.length === 0} onClick={() => setActiveStep((v) => Math.min(sections.length - 1, v + 1))}>{copy.next}</Btn>
            </>
          ) : (
            <span
              title={missingRequiredRows.length ? copy.fillRequired.replace('{count}', missingRequiredRows.length) : ''}
              onClick={missingRequiredRows.length && !saving ? () => doSave(true) : undefined}
              style={{ display: 'inline-flex', cursor: missingRequiredRows.length ? 'not-allowed' : 'default' }}
            >
              <Btn
                variant="primary"
                icon={saving ? 'loader-circle' : 'check'}
                disabled={saving || missingRequiredRows.length > 0}
                aria-disabled={missingRequiredRows.length > 0}
                style={missingRequiredRows.length ? { opacity: .55, cursor: 'not-allowed', pointerEvents: 'none' } : undefined}
                onClick={() => doSave(true)}
              >
                {saving ? copy.saving : copy.save}
              </Btn>
            </span>
          )}
        </>
      )}
    >
      {error && <Alert tone="bad" icon="circle-alert" title={error} />}
      {formKey === 'blue_card' && activeIndex === 0 && (
        <Alert tone="info" icon="info" title={t('form.blueCardIntroTitle')}>
          {t('form.blueCardIntroBody')}
        </Alert>
      )}
      <div className="wizard-progress-block mb-14">
        <div className="wizard-progress-copy">
          <span>{copy.step} <strong>{activeIndex + 1}/{Math.max(sections.length, 1)}</strong></span>
          <span>{progress}%</span>
        </div>
        <div className="wizard-progress-track"><span style={{ width: `${progress}%` }} /></div>
      </div>
      <div className="application-step-strip mb-14" style={{ display: 'flex', gap: 8, overflowX: 'auto', paddingBottom: 4 }}>
        {sections.map((section, index) => {
          const state = index < activeIndex ? 'done' : index === activeIndex ? 'current' : 'todo';
          return (
            <button
              key={section.id}
              type="button"
              onClick={() => setActiveStep(index)}
              aria-current={index === activeIndex ? 'step' : undefined}
              style={{
                minWidth: 150,
                maxWidth: 210,
                flex: '0 0 auto',
                border: '1px solid ' + (state === 'current' ? 'var(--brand-400)' : 'var(--line-2)'),
                background: state === 'current' ? 'var(--brand-50)' : state === 'done' ? 'var(--surface-2)' : 'var(--surface)',
                color: state === 'current' ? 'var(--brand-700)' : 'var(--ink-700)',
                borderRadius: 'var(--r-sm)',
                padding: '10px 11px',
                textAlign: 'left',
                cursor: 'pointer',
              }}
            >
              <div className="flex ac gap-8">
                <span className="mono" style={{ fontSize: 11, fontWeight: 850 }}>{String(index + 1).padStart(2, '0')}</span>
                {state === 'done' && <Icon name="check" size={13} />}
              </div>
              <div style={{ marginTop: 5, fontSize: 12, fontWeight: 750, lineHeight: 1.25, whiteSpace: 'normal' }}>{section.title}</div>
            </button>
          );
        })}
      </div>
      <div className="flex col gap-14">
        {!activeSection ? (
          <Card><div className="card-bd"><div className="muted" style={{ padding: 18, textAlign: 'center' }}>—</div></div></Card>
        ) : (() => {
          const sectionFilled = activeRows.filter((row) => isFilledContactValue(getValue(row))).length;
          const [personalRows, addressRows] = activeFamilyMember ? partitionFamilyRows(activeRows) : [activeRows, []];
          const addressChoice = activeFamilyMember && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 9, padding: '11px 10px', borderBottom: '1px solid var(--line-2)' }}>
              {activeFamilyMember.type === 'sibling' ? (
                <fieldset style={{ display: 'flex', flexDirection: 'column', gap: 8, margin: 0, padding: 0, border: 0 }}>
                  <legend className="muted" style={{ marginBottom: 2, padding: 0, fontSize: 11.5, fontWeight: 700 }}>
                    {t('wizard.sibling.addr.label')}
                  </legend>
                  {[
                    ['mine', 'wizard.sibling.addr.mine'],
                    ['parents', 'wizard.sibling.addr.parents'],
                    ['different', 'wizard.sibling.addr.diff'],
                  ].map(([value, labelKey]) => (
                    <label key={value} className="flex ac gap-8" style={{ fontSize: 13, fontWeight: 650, cursor: 'pointer' }}>
                      <input
                        type="radio"
                        name={`sibling-address-${activeFamilyMember.key}`}
                        value={value}
                        checked={activeSiblingAddressChoice === value}
                        onChange={() => updateSiblingAddress(value)}
                      />
                      <span>{t(labelKey)}</span>
                    </label>
                  ))}
                </fieldset>
              ) : (
                <label className="flex ac gap-8" style={{ fontSize: 13, fontWeight: 650, cursor: 'pointer' }}>
                  <input type="checkbox" checked={activeSameAddress} onChange={(event) => updateSameAddress(event.target.checked)} />
                  <span>{sameAddressLabel}</span>
                </label>
              )}
            </div>
          );
          return (
            <Card key={activeSection.id} style={{ overflow: 'hidden' }}>
              <div style={{ padding: '14px 20px', borderBottom: '1px solid var(--line-2)', fontWeight: 750, fontSize: 13, color: 'var(--ink-900)', letterSpacing: '.04em' }}>
                {upperUiLabel(activeSection.title, lang)}
                <span className="muted mono" style={{ fontSize: 11.5, fontWeight: 600, marginLeft: 8 }}>{sectionFilled}/{activeRows.length}</span>
              </div>
              <div style={{ padding: '6px 20px 12px' }}>
                {personalRows.map((row) => (
                  <ContactRow
                    key={`${activeSection.id}-${row.key}-${row.l}`}
                    row={row}
                    editing
                    editValue={answers[row.key]}
                    onEditChange={(key, value) => updateAnswer(row, value)}
                    error={validationAttempted && missingRequiredKeys.has(row.key) ? copy.required : ''}
                  />
                ))}
                {addressChoice}
                {addressRows.map((row) => (
                  <ContactRow
                    key={`${activeSection.id}-${row.key}-${row.l}`}
                    row={row}
                    editing
                    editValue={answers[row.key]}
                    onEditChange={(key, value) => updateAnswer(row, value)}
                    error={validationAttempted && missingRequiredKeys.has(row.key) ? copy.required : ''}
                  />
                ))}
              </div>
            </Card>
          );
        })()}
      </div>
    </Modal>
  );
}

function DataAccordionCard({ title, sub, count, children }) {
  const [open, setOpen] = useState(false);
  return (
    <Card className="data-accordion">
      <button type="button" className="data-acc-toggle" onClick={() => setOpen((v) => !v)} aria-expanded={open}>
        <Icon name={open ? 'chevron-down' : 'chevron-right'} size={18} className="dim" />
        <div className="data-acc-title">
          <strong>{title}</strong>
          {sub && <span>{sub}</span>}
        </div>
        {count && <span className="data-acc-count">{count}</span>}
      </button>
      {open && <div className="data-acc-body">{children}</div>}
    </Card>
  );
}

function MyDataSummaryCard({ rows, onOpenSavedData }) {
  const { t } = useT();
  return (
    <Card className="my-data-summary mb-18">
      <div className="card-bd">
        <div className="flex jb ac wrap gap-12 mb-8">
          <div className="h3">{t('myData.summary.title')}</div>
          <Btn variant="ghost" icon="database" onClick={onOpenSavedData}>{t('myData.savedDataButton')}</Btn>
        </div>
        <div className="my-data-summary-rows">
          {rows.map((row) => (
            <div className="my-data-summary-row" key={row.key}>
              <span className={'my-data-summary-icon ' + (row.complete ? 'is-complete' : 'is-missing')}><Icon name={row.icon} size={17} /></span>
              <strong>{row.label}</strong>
              <span className={'my-data-summary-value' + (row.complete ? '' : ' is-placeholder')}>{row.value}</span>
              {row.status && <span className={'my-data-summary-status ' + (row.complete ? 'is-complete' : 'is-missing')}>{row.status}</span>}
            </div>
          ))}
        </div>
      </div>
    </Card>
  );
}

function FormSubmissionPage({ ct, contact, fieldMappings, onRefresh, onOptimisticUpdate, syncState, purchasedServices, openModal, onCompleteProfile }) {
  const { t, lang } = useT();
  const authEmail = (window.YEYE_AUTH && window.YEYE_AUTH.getState && (window.YEYE_AUTH.getState().user || {}).email) || '';
  const isAdmin = /@yeyeagency\.com$/i.test(authEmail);
  const [activeSection, setActiveSection] = useState('all');
  const [activeForm, setActiveForm] = useState('');
  const [onlyMissing, setOnlyMissing] = useState(false);
  const [isEditing, setIsEditing] = useState(false);
  const [edits, setEdits] = useState({});
  const [saving, setSaving] = useState(false);
  const [profileWizardOpen, setProfileWizardOpen] = useState(false);
  const [profileWizardSection, setProfileWizardSection] = useState(null);
  const [formModalKey, setFormModalKey] = useState('');
  const [savedDataOpen, setSavedDataOpen] = useState(false);
  const serviceKeys = new Set([
    ct && ct.key,
    ...((purchasedServices || []).map((item) => item && (item.k || item.key || item.serviceKey || item.productKey)).filter(Boolean)),
    ...((contact && contact.tags) || []),
  ].map((key) => String(key || '').toLowerCase()));
  const paidFormKeys = paidApplicationFormKeys(purchasedServices, contact);
  const hasPaidServiceAccess = paidFormKeys.length > 0 || (purchasedServices || []).some((item) => {
    const status = String(item?.status || 'active').toLowerCase();
    return !['cancelled', 'canceled', 'inactive', 'refunded'].includes(status);
  });
  const formKey = paidFormKeys[0] || '';
  const activeFormKey = activeForm || formKey;
  const rows = activeFormKey && window.YEYE_FIELD_MAPPING
    ? window.YEYE_FIELD_MAPPING.forForm(fieldMappings || [], activeFormKey, contact || {})
    : [];
  const filled = rows.filter((row) => row.value);
  const missing = rows.filter((row) => !row.value);
  const profileMissing = window.YEYE_PROFILE && window.YEYE_PROFILE.missing
    ? window.YEYE_PROFILE.missing(contact || {}, isEditing ? edits : {})
    : [];
  const displayedValue = (row) => (
    Object.prototype.hasOwnProperty.call(edits, row.key) ? edits[row.key] : row.v
  );
  const getAnswer = (key) => rowLookupValue(rows, edits, [key]) || edits[key] || '';
  const allSections = buildContactCardSections(contact || {}, rows, activeFormKey, lang, t, getAnswer);
  const visibleSections = allSections
    .filter((section) => visibleApplicationSection(section, getAnswer))
    .map((section) => ({ ...section, rows: visibleApplicationRows(section, getAnswer) }))
    .filter((section) => activeSection === 'all' || section.filterId === activeSection || section.id === activeSection)
    .map((section) => ({ ...section, rows: onlyMissing ? section.rows.filter((row) => !isFilledContactValue(displayedValue(row))) : section.rows }))
    .filter((section) => section.rows.length);
  const availableFormKeys = paidFormKeys.length ? paidFormKeys : [];
  const formChips = availableFormKeys.map((key) => ({ key, label: applicationFormLabel(key, lang) }));
  const contactName = contactDisplayName(contact || {});
  const contactEmail = contact?.email || contactFieldValue(contact, ['email', 'contact.email']);
  const contactPhone = contactFieldValue(contact, ['phone', 'contact.phone']);
  const fc = {
    en: { customers: 'Customers', active: 'Active', missingFields: 'Missing Fields', edit: 'Edit', linkedCompany: 'Linked Company · GHL business', companyDetail: 'Company Detail', syncWarning: 'GHL sync warning', all: 'All', contact: 'Contact', personal: 'Personal Information', company: 'Employer Information', addresses: 'Address Information', passport: 'Passport / Travel Document', lastEmployment: 'Last Employment', family: 'Family Information', approvals: 'Approvals', others: 'Others', syncing: 'Syncing', lastSync: 'Last sync', onlyMissing: 'Only Missing', submittedForms: 'Submitted Forms' },
    tr: { customers: 'Müşteriler', active: 'Aktif', missingFields: 'Eksik Alanlar', edit: 'Düzenle', linkedCompany: 'Bağlı Firma · GHL business', companyDetail: 'Firma Detayı', syncWarning: 'GHL sync uyarısı', all: 'Tümü', contact: 'İletişim', personal: 'Kişisel Bilgiler', company: 'İşveren Bilgileri', addresses: 'Adres Bilgileri', passport: 'Pasaport / Seyahat Belgesi', lastEmployment: 'Son İstihdam', family: 'Aile Bilgileri', approvals: 'Onaylar', others: 'Diğerleri', syncing: 'Senkronize', lastSync: 'Son sync', onlyMissing: 'Sadece Eksikler', submittedForms: 'Submit Edilen Formlar' },
    cs: { customers: 'Zákazníci', active: 'Aktivní', missingFields: 'Chybějící pole', edit: 'Upravit', linkedCompany: 'Propojená firma · GHL business', companyDetail: 'Detail firmy', syncWarning: 'Upozornění synchronizace GHL', all: 'Vše', contact: 'Kontakt', personal: 'Osobní údaje', company: 'Údaje zaměstnavatele', addresses: 'Adresní údaje', passport: 'Pas / cestovní doklad', lastEmployment: 'Poslední zaměstnání', family: 'Rodinné údaje', approvals: 'Souhlasy', others: 'Ostatní', syncing: 'Synchronizuje se', lastSync: 'Poslední sync', onlyMissing: 'Pouze chybějící', submittedForms: 'Odeslané formuláře' },
  }[lang] || {};
  const companyName = contactFieldValue(contact, [
    'business.name', 'business.companyName', 'companyName', 'company_name', 'contact.company_name', 'contact.employer_name'
  ]);
  const companyMeta = [
    contactFieldValue(contact, ['business.city', 'company_city', 'contact.company_city']),
    contactFieldValue(contact, ['business.country', 'company_country', 'contact.company_country']),
    contactFieldValue(contact, ['business.status', 'company_status'])
  ].filter(Boolean).join(' · ');
  const companyPosition = contactFieldValue(contact, ['contactemployee_job_position', 'job_position', 'position', 'contact.position']);
  const summaryAddress = [
    contactFieldValue(contact, ['city', 'contact.city', 'municipality_in_czechia', 'resMunicipality']),
    contactFieldValue(contact, ['country', 'contact.country', 'country_residence', 'resCountry']),
  ].filter(Boolean).join(', ');
  const summaryPostal = contactFieldValue(contact, ['postalCode', 'contact.postalCode', 'postalzip_code_in_czechia', 'resPostalCode', 'zip_code']);
  const summaryTracker = window.PROCESS_TRACKER || {};
  const summaryFormType = summaryTracker.detectFormType ? summaryTracker.detectFormType(contact || {}) : null;
  const summaryDocStatus = summaryFormType && summaryTracker.documentStatusFromContact
    ? summaryTracker.documentStatusFromContact(contact || {}, summaryFormType)
    : { items: [], total: 0, doneCount: 0 };
  const summaryPassportItem = (summaryDocStatus.items || []).find((item) => item.key === 'passport_visa_copy');
  const summaryPassportUploaded = !!(summaryPassportItem && (summaryPassportItem.completed || summaryPassportItem.url));
  const summaryDocsConfirmed = Number(summaryDocStatus.doneCount || 0);
  const summaryDocsPending = Math.max(0, Number(summaryDocStatus.total || 0) - summaryDocsConfirmed);
  const summaryRows = [
    {
      key: 'personal', icon: 'user-round', label: t('myData.summary.personal'),
      value: [contactName, contactEmail, contactPhone].filter(Boolean).join(' · ') || t('myData.summary.notProvided'),
      complete: !!(contactName || contactEmail || contactPhone),
    },
    {
      key: 'address', icon: 'house', label: t('myData.summary.address'),
      value: [summaryAddress, summaryPostal].filter(Boolean).join(' · ') || t('myData.summary.notProvided'),
      complete: !!(summaryAddress || summaryPostal),
    },
    {
      key: 'passport', icon: 'book-user', label: t('myData.summary.passport'),
      value: summaryPassportUploaded ? t('myData.summary.uploaded') : t('myData.summary.missing'),
      complete: summaryPassportUploaded,
      status: summaryPassportUploaded ? '✓' : '⚠',
    },
    {
      key: 'company', icon: 'building-2', label: t('myData.summary.company'),
      value: [companyName, companyPosition].filter(Boolean).join(' · ') || t('myData.summary.notProvided'),
      complete: !!(companyName || companyPosition),
    },
    {
      key: 'documents', icon: 'files', label: t('myData.summary.documents'),
      value: summaryDocStatus.total
        ? `${summaryDocsConfirmed} ${t('myData.summary.confirmed')} · ${summaryDocsPending} ${t('myData.summary.pending')}`
        : t('myData.summary.notProvided'),
      complete: summaryDocStatus.total > 0 && summaryDocsPending === 0,
    },
  ];
  const primaryFilters = [
    { id: 'all', label: fc.all },
    { id: 'contact_kisisel_iletisim', label: fc.contact },
    { id: 'personal_information', label: fc.personal },
    { id: 'company', label: fc.company },
    { id: 'addresses', label: fc.addresses },
    { id: 'passport_travel_document', label: fc.passport },
  ].filter((filter) => filter.id === 'all' || allSections.some((section) => section.filterId === filter.id || section.id === filter.id));
  const otherFilters = [
    { id: 'last_employment', label: fc.lastEmployment },
    { id: 'family_information', label: fc.family },
    { id: 'approvals', label: fc.approvals },
  ].filter((filter) => allSections.some((section) => section.filterId === filter.id || section.id === filter.id));
  const otherActive = otherFilters.some((filter) => filter.id === activeSection);
  const applyOptimisticUpdate = (patch) => {
    const contactId = contact && (contact.contactId || contact.id);
    if (!contactId || !patch) return;
    if (onOptimisticUpdate) {
      onOptimisticUpdate(contactId, patch);
      return;
    }
    if (window.YEYE_CONTACT_STATE && window.YEYE_CONTACT_STATE.applyOptimisticUpdate) {
      window.YEYE_CONTACT_STATE.applyOptimisticUpdate(contactId, patch);
    }
  };

  useEffect(() => {
    if (!onRefresh) return undefined;
    let cancelled = false;
    onRefresh();
    const timer = setInterval(() => { if (!cancelled) onRefresh(); }, 15000);
    return () => { cancelled = true; clearInterval(timer); };
  }, [formKey]);

  useEffect(() => {
    if (formKey) setActiveForm(formKey);
  }, [formKey]);

  return (
    <>
      <Card className="mb-18" style={{ overflow: 'hidden' }}>
        <div className="card-bd">
          <div className="flex ac wrap gap-16">
            <Avatar size="lg" tone={{ bg: 'var(--brand-400)', fg: '#fff' }}>{contactInitials(contact || {})}</Avatar>
            <div className="grow" style={{ minWidth: 260 }}>
              <div className="flex ac gap-8 wrap">
                <div className="h2">{contactName}</div>
              </div>
              <div className="muted mono" style={{ fontSize: 12.5, marginTop: 5 }}>{[contactEmail, contactPhone].filter(Boolean).join(' · ') || '—'}</div>
            </div>
            <div className="flex ac gap-8 wrap" style={{ justifyContent: 'flex-end' }}>
              {hasPaidServiceAccess && <Btn variant={onlyMissing ? 'primary' : 'ghost'} icon="circle-alert" onClick={() => setOnlyMissing((v) => !v)}>{fc.missingFields}</Btn>}
              {hasPaidServiceAccess && <Btn variant="ghost" icon="upload" onClick={() => openModal && openModal('upload', {
                contactId: contact && (contact.contactId || contact.id),
                folderK: 'f_identity',
                subName: folderTkToSubName('f_identity'),
                onUploaded: async () => {
                  if (onRefresh) await Promise.resolve(onRefresh());
                  if (window.YEYE_TOAST) window.YEYE_TOAST(t('doc.uploaded'));
                },
              })}>{t('auth.passportUpload')}</Btn>}
              {hasPaidServiceAccess && !isEditing && <Btn variant="ghost" icon="clipboard-list" onClick={() => {
                if (activeFormKey) {
                  setFormModalKey(activeFormKey);
                  return;
                }
                setProfileWizardSection(null);
                setProfileWizardOpen(true);
              }}>{t('wizard.openFullForm')}</Btn>}
              {isEditing ? (
                <>
                  <Btn variant="ghost" icon="x" disabled={saving} onClick={() => { setEdits({}); setIsEditing(false); }}>{t('c.cancel')}</Btn>
                  <Btn variant="primary" icon={saving ? 'loader-circle' : 'check'} disabled={saving || Object.keys(edits).length === 0} onClick={async () => {
                    const contactId = contact && (contact.contactId || contact.id);
                    const emailAddr = (contact && contact.email) || '';
                    if (!contactId || !emailAddr) { showToast && showToast('Contact not connected'); return; }
                    const missingAfterSave = window.YEYE_PROFILE && window.YEYE_PROFILE.missing
                      ? window.YEYE_PROFILE.missing(contact || {}, edits)
                      : [];
                    setSaving(true);
                    const allowed = new Set(['firstName', 'lastName', 'email', 'phone', 'address1', 'city', 'state', 'country', 'postalCode', 'companyName', 'website', 'dateOfBirth']);
                    const basic = {};
                    const customs = [];
                    Object.entries(edits).forEach(([k, v]) => {
                      const meta = rows.find((r) => (r.updateKey === k || r.fieldName === k || r.key === k));
                      const requiredMeta = (window.YEYE_REQUIRED_FIELDS || []).find((entry) => (
                        entry.key === k ||
                        String(entry.key).replace(/^contact\./, '') === String(k).replace(/^contact\./, '')
                      ));
                      const gid = (meta && (realGhlFieldId(meta, activeFormKey) || consentGhlFieldId(contact, meta))) ||
                        (requiredMeta && requiredMeta.ghlFieldId);
                      const shortKey = String(k).replace(/^contact\./, '').replace(/^business\./, '');
                      const basicKey = meta ? applicationBasicKey(meta) : (allowed.has(shortKey) ? shortKey : '');
                      if (basicKey && !gid) basic[basicKey] = v;
                      else if (gid) customs.push({ id: gid, ghlFieldId: gid, key: k, value: v });
                    });
                    try {
                      if (!window.YEYE_BACKEND || !window.YEYE_BACKEND.updateContactProfile) throw new Error('write-backend-not-configured');
                      await window.YEYE_BACKEND.updateContactProfile({ contactId, basic, customFields: customs });
                      applyOptimisticUpdate({
                        updates: basic,
                        customFieldUpdates: customs.map((field) => ({
                          id: field.id,
                          key: field.key,
                          value: field.value,
                        })),
                      });
                      auditFormSave(contact, activeFormKey, edits, rows, allSections, 'form_submission');
                      setEdits({});
                      setIsEditing(false);
                      if (onRefresh) onRefresh();
                      window.YEYE_TOAST && window.YEYE_TOAST(t('form.savedGhl') || 'Saved');
                      if (hasPaidServiceAccess && missingAfterSave.length) {
                        window.YEYE_TOAST && window.YEYE_TOAST(
                          t('profileFields.missingBanner').replace('{count}', missingAfterSave.length)
                        );
                      }
                    } catch (err) {
                      console.warn('Save failed', err);
                      window.YEYE_TOAST && window.YEYE_TOAST((err && err.message) || 'Save failed');
                    } finally {
                      setSaving(false);
                    }
                  }}>{saving ? t('c.saving') : (t('form.save') || 'Save')}</Btn>
                </>
              ) : (
                <Btn variant="ghost" icon="pencil" onClick={() => setIsEditing(true)}>{fc.edit}</Btn>
              )}
            </div>
          </div>
        </div>
      </Card>
      {!savedDataOpen ? (
        <MyDataSummaryCard rows={summaryRows} onOpenSavedData={() => setSavedDataOpen(true)} />
      ) : (
        <Card className="mb-18">
          <div className="card-bd flex jb ac wrap gap-12">
            <div className="h2">{t('myData.savedDataTitle')}</div>
            <Btn variant="ghost" icon="arrow-left" onClick={() => setSavedDataOpen(false)}>{t('c.back')}</Btn>
          </div>
        </Card>
      )}
      {savedDataOpen && !hasPaidServiceAccess && (
        <div className="flex col gap-16">
          <DataAccordionCard
            title={lang === 'tr' ? 'Kayıt Bilgileri' : (lang === 'cs' ? 'Registrační údaje' : 'Registration Details')}
            sub={lang === 'tr' ? 'Servis satın alınana kadar sadece temel iletişim bilgileri tutulur.' : 'Only basic contact details are kept until a service is purchased.'}
            count="4"
          >
            {[
              { key: 'firstName', l: t('wizard.field.firstName'), v: contactFieldValue(contact, ['firstName', 'contact.firstName']) },
              { key: 'lastName', l: t('wizard.field.lastName'), v: contactFieldValue(contact, ['lastName', 'contact.lastName']) },
              { key: 'email', l: t('wizard.field.email'), v: contactEmail },
              { key: 'phone', l: t('wizard.field.phone'), v: contactPhone },
            ].map((row) => (
              <ContactRow
                key={row.key}
                row={row}
                editing={isEditing}
                editValue={edits[row.key]}
                onEditChange={(k, v) => setEdits((prev) => ({ ...prev, [k]: v }))}
              />
            ))}
          </DataAccordionCard>
        </div>
      )}
      {!savedDataOpen && hasPaidServiceAccess && profileMissing.length > 0 && (
        <div className="mb-18">
          <ProfileMissingFieldsBanner contact={contact} edits={isEditing ? edits : {}} />
        </div>
      )}
      {!savedDataOpen && hasPaidServiceAccess && <Card className="mb-18">
        <div className="card-bd">
          <div className="flex jb ac wrap gap-12">
            <div>
              <div className="eyebrow mb-6">{t('myData.internalForm.eyebrow')}</div>
              <div className="strong" style={{ fontSize: 15 }}>
                {t(availableFormKeys.length ? 'myData.internalForm.paidTitle' : 'myData.internalForm.emptyTitle')}
              </div>
              <div className="muted" style={{ fontSize: 12.5, marginTop: 4 }}>
                {t(availableFormKeys.length ? 'myData.internalForm.paidBody' : 'myData.internalForm.emptyBody')}
              </div>
            </div>
            <div className="flex ac gap-8 wrap" style={{ justifyContent: 'flex-end' }}>
              {formChips.map((chip) => (
                <Btn
                  key={chip.key}
                  variant={activeFormKey === chip.key ? 'primary' : 'ghost'}
                  icon="clipboard-list"
                  onClick={() => { setActiveForm(chip.key); setActiveSection('all'); setFormModalKey(chip.key); }}
                >
                  {chip.label}
                </Btn>
              ))}
            </div>
          </div>
        </div>
      </Card>}
      {!savedDataOpen && hasPaidServiceAccess && <DocGenerationCard contact={contact} purchasedServices={purchasedServices} onGenerated={onRefresh || (() => {})} onCompleteProfile={onCompleteProfile} />}
      {!savedDataOpen && hasPaidServiceAccess && companyName && (
        <Card className="mb-18" style={{ borderColor: 'var(--accent-300)' }}>
          <div className="card-bd">
            <div className="flex ac gap-14">
              <Avatar tone={{ bg: 'var(--accent-100)', fg: 'var(--accent-700)' }}>{String(companyName).slice(0, 2).toUpperCase()}</Avatar>
              <div className="grow">
                <div className="eyebrow">{fc.linkedCompany}</div>
                <div className="strong" style={{ fontSize: 15 }}>{companyName}</div>
                {companyMeta && <div className="muted mono" style={{ fontSize: 11.5 }}>{companyMeta}</div>}
              </div>
            </div>
          </div>
        </Card>
      )}
      {!savedDataOpen && hasPaidServiceAccess && isAdmin && syncState && syncState.error && (
        <Alert tone="warn" icon="triangle-alert" title={fc.syncWarning}>{syncState.error}</Alert>
      )}
      {savedDataOpen && hasPaidServiceAccess && (rows.length === 0 ? (
        <div className="flex col gap-16">
          {(() => {
            const reqSections = window.YEYE_REQUIRED_SECTIONS || {};
            const reqFields = window.YEYE_REQUIRED_FIELDS || [];
            const visibleReqFields = onlyMissing && window.YEYE_PROFILE && window.YEYE_PROFILE.missing
              ? window.YEYE_PROFILE.missing(contact || {}, isEditing ? edits : {})
              : reqFields;
            const grouped = {};
            visibleReqFields.forEach((entry) => {
              (grouped[entry.section] = grouped[entry.section] || []).push(entry);
            });
            return Object.keys(grouped).map((sectionKey) => {
              const secMeta = reqSections[sectionKey] || {};
              const secTitle = secMeta[lang] || secMeta.en || sectionKey;
              const secRows = grouped[sectionKey].map((entry) => {
                const raw = window.YEYE_PROFILE && window.YEYE_PROFILE.getValue
                  ? window.YEYE_PROFILE.getValue(contact || {}, entry)
                  : null;
                const labelKey = 'wizard.field.' + entry.key;
                return {
                  l: t(labelKey),
                  v: formatFieldValue(raw),
                  key: entry.key,
                };
              });
              const filledCount = secRows.filter((row) => isFilledContactValue(row.v)).length;
              return (
                <DataAccordionCard
                  key={sectionKey}
                  title={upperUiLabel(secTitle, lang)}
                  count={`${filledCount}/${secRows.length}`}
                >
                    {secRows.map((row) => (
                      <ContactRow
                        key={`${sectionKey}-${row.key}-${row.l}`}
                        row={row}
                        editing={isEditing}
                        editValue={edits[row.key]}
                        onEditChange={(k, v) => setEdits((prev) => ({ ...prev, [k]: v }))}
                      />
                    ))}
                </DataAccordionCard>
              );
            });
          })()}
        </div>
      ) : (
        <>
          <div className="flex ac gap-7 wrap mb-18">
            {primaryFilters.map((filter) => (
              <button key={filter.id} className={'btn btn-sm ' + (activeSection === filter.id ? 'btn-primary' : 'btn-ghost')} onClick={() => setActiveSection(filter.id)}>{filter.label}</button>
            ))}
            {otherFilters.length > 0 && (
              <select value={otherActive ? activeSection : ''} onChange={(e) => setActiveSection(e.target.value || 'all')} className={'btn btn-sm data-filter-select ' + (otherActive ? 'btn-primary' : 'btn-ghost')} style={{ height: 34 }}>
                <option value="">{fc.others}</option>
                {otherFilters.map((filter) => <option key={filter.id} value={filter.id}>{filter.label}</option>)}
              </select>
            )}
            <div className="grow" />
          </div>

          <div className="flex col gap-16">
            {visibleSections.length === 0 ? (
              <Card><div className="card-bd"><div className="muted" style={{ padding: 18, textAlign: 'center' }}>—</div></div></Card>
            ) : visibleSections.map((section) => {
              const sectionFilled = section.rows.filter((row) => isFilledContactValue(row.v)).length;
              return (
                <DataAccordionCard
                  key={section.id}
                  title={upperUiLabel(section.title, lang)}
                  count={`${sectionFilled}/${section.rows.length}`}
                >
                    {section.rows.map((row) => (
                      <ContactRow
                        key={`${section.id}-${row.key}-${row.l}`}
                        row={row}
                        editing={isEditing}
                        editValue={edits[row.key]}
                        onEditChange={(k, v) => setEdits((prev) => ({ ...prev, [k]: v }))}
                      />
                    ))}
                </DataAccordionCard>
              );
            })}
          </div>
        </>
      ))}
      {savedDataOpen && hasPaidServiceAccess && <div className="mb-18" style={{ marginTop: 18 }}>
        <DataAccordionCard title={t('myData.docs.title')} sub={t('myData.docs.sub')} count="8">
          <div className="grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill,minmax(230px,1fr))', gap: 10 }}>
            {[
              { key: 'passport',        folderK: 'f_identity',        subK: 'sf_passport',            icon: 'book-user' },
              { key: 'criminal',        folderK: 'f_identity',        subK: 'sf_criminal_record',     icon: 'shield-alert' },
              { key: 'biometric',       folderK: 'f_identity',        subK: 'sf_passport',            icon: 'camera' },
              { key: 'diploma',         folderK: 'f_education',       subK: 'sf_diploma',             icon: 'graduation-cap' },
              { key: 'employment',      folderK: 'f_relocation',      subK: 'sf_employment_contract', icon: 'file-signature' },
              { key: 'accommodation',   folderK: 'f_accommodation_re',subK: 'sf_lease',               icon: 'home' },
              { key: 'payslip',         folderK: 'f_relocation',      subK: 'sf_payslip',             icon: 'receipt' },
              { key: 'applicationFee',  folderK: 'f_relocation',      subK: 'sf_blue_employee',       icon: 'wallet' },
            ].map((d) => (
              <button
                key={d.key}
                type="button"
                onClick={() => openModal && openModal('upload', {
                  contactId: contact && (contact.contactId || contact.id),
                  folderK: d.folderK,
                  subName: folderTkToSubName(d.folderK),
                  onUploaded: async () => {
                    if (onRefresh) await Promise.resolve(onRefresh());
                    if (window.YEYE_TOAST) window.YEYE_TOAST(t('doc.uploaded'));
                  },
                })}
                style={{
                  display: 'flex', alignItems: 'center', gap: 10, textAlign: 'left',
                  padding: '11px 12px', borderRadius: 12, border: '1px solid var(--line)',
                  background: 'var(--surface)', color: 'var(--ink-800)', cursor: 'pointer',
                  fontFamily: 'inherit', fontSize: 13, fontWeight: 700, minWidth: 0,
                }}
              >
                <span style={{ width: 34, height: 34, borderRadius: 10, background: 'var(--brand-50)', color: 'var(--brand-600)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
                  <Icon name={d.icon} size={16} />
                </span>
                <span style={{ flex: 1, minWidth: 0, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                  {t('myData.doc.' + d.key)}
                </span>
                <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, color: 'var(--accent-600)', fontSize: 12, fontWeight: 800 }}>
                  <Icon name="upload" size={13} />{t('myData.doc.upload')}
                </span>
              </button>
            ))}
          </div>
        </DataAccordionCard>
      </div>}
      {hasPaidServiceAccess && profileWizardOpen && (
        <ProfileWizard
          contact={contact}
          onClose={() => setProfileWizardOpen(false)}
          initialSection={profileWizardSection}
          onSaved={() => {
            setProfileWizardOpen(false);
            if (onRefresh) onRefresh();
          }}
        />
      )}
      {hasPaidServiceAccess && formModalKey && (
        <ApplicationFormModal
          formKey={formModalKey}
          contact={contact}
          rows={window.YEYE_FIELD_MAPPING ? window.YEYE_FIELD_MAPPING.forForm(fieldMappings || [], formModalKey, contact || {}) : []}
          lang={lang}
          t={t}
          onClose={() => setFormModalKey('')}
          onSaved={() => { if (onRefresh) onRefresh(); }}
          applyOptimisticUpdate={applyOptimisticUpdate}
        />
      )}
    </>
  );
}

/* ===== APPOINTMENTS — Calendar (June 2026) ===== */
const CAL_EVENTS = [
  { d: 9, key: 'police', time: '09:30', tone: 'ok', ic: 'shield' },
  { d: 14, key: 'deadline', time: '23:59', tone: 'bad', ic: 'file-warning' },
  { d: 19, key: 'biometrics', time: '11:00', tone: 'info', ic: 'fingerprint' },
  { d: 27, key: 'consult', time: '10:00', tone: 'brand', ic: 'video' },
];
const TODAY = 7, MONTH_IDX = 5, FIRST_DOW = 0, DAYS_IN = 30;
function evTone(tone) {
  const m = { ok: ['var(--ok-bg)', 'var(--ok)'], info: ['var(--info-bg)', 'var(--info)'], bad: ['var(--bad-bg)', 'var(--bad)'], brand: ['var(--brand-500)', '#fff'] };
  const [bg, fg] = m[tone] || m.ok; return { background: bg, color: fg };
}
function ApptDetail({ e, title, loc, dim }) {
  return (
    <div style={{ border: '1px solid var(--line)', borderRadius: 'var(--r-md)', padding: 14 }}>
      <div className="flex ac gap-10 mb-12">
        <div className="l-ic" style={{ ...evTone(e.tone), width: 36, height: 36 }}><Icon name={e.ic} size={18} /></div>
        <div className="grow"><div className="strong" style={{ fontSize: 14 }}>{title}</div></div>
      </div>
      <div className="flex col gap-8">
        <div className="flex ac gap-8 muted" style={{ fontSize: 12.5 }}><Icon name="clock" size={14} /> {e.time}</div>
        <div className="flex ac gap-8 muted" style={{ fontSize: 12.5 }}><Icon name="map-pin" size={14} /> {loc}</div>
      </div>
      <Btn variant="ghost" size="sm" className="btn-block mt-12" iconR="arrow-right">{dim}</Btn>
    </div>
  );
}
const APPT_CATEGORIES = [
  { key: 'immigration', icon: 'shield', match: (e) => ['police', 'biometrics', 'ministry', 'immigration'].includes(e.key) },
  { key: 'medical', icon: 'stethoscope', match: (e) => ['medical', 'health', 'checkup'].includes(e.key) },
  { key: 'document', icon: 'file-text', match: (e) => ['document', 'signing', 'notary'].includes(e.key) },
  { key: 'personal', icon: 'user-round', match: (e) => ['custom', 'google', 'personal'].includes(e.key) },
  { key: 'other', icon: 'circle-dashed', match: () => true },
];

function apptCategoryOf(e) {
  if (e.category) return e.category;
  for (const c of APPT_CATEGORIES) if (c.match(e)) return c.key;
  return 'other';
}

function AppointmentsPage({ events, openModal }) {
  const { t, dict } = useT();
  const [view, setView] = useState('month');
  const [sel, setSel] = useState(9);
  const [filter, setFilter] = useState('all');
  const allRows = (events || CAL_EVENTS).map((e) => ({ ...e, category: apptCategoryOf(e) }));
  const rows = filter === 'all' ? allRows : allRows.filter((e) => e.category === filter);
  const evByDay = {};
  rows.forEach((e) => { (evByDay[e.d] = evByDay[e.d] || []).push(e); });
  const selEvents = evByDay[sel] || [];
  const chipStyle = (active) => ({
    display: 'inline-flex', alignItems: 'center', gap: 6, padding: '6px 11px', borderRadius: 999,
    border: '1px solid ' + (active ? 'var(--brand-500)' : 'var(--line)'),
    background: active ? 'var(--brand-50)' : 'var(--surface)', color: active ? 'var(--brand-700)' : 'var(--ink-700)',
    fontSize: 12.5, fontWeight: 600, cursor: 'pointer', whiteSpace: 'nowrap',
  });
  const cells = [];
  for (let i = 0; i < FIRST_DOW; i++) cells.push(null);
  for (let d = 1; d <= DAYS_IN; d++) cells.push(d);
  while (cells.length % 7 !== 0) cells.push(null);
  const dow = (d) => dict.weekdays[(FIRST_DOW + d - 1) % 7];
  const titleOf = (e) => e.title || t('ev.' + e.key);
  const locOf = (e) => e.loc || t('ev.' + e.key + 'Loc');

  return (
    <>
      <PageHead title={t('nav.appointments')} sub={t('sub.appointments')}
        actions={<><Seg items={[{ value: 'month', label: t('c.month') }, { value: 'list', label: t('c.list') }]} value={view} onChange={setView} /><Btn variant="primary" icon="calendar-plus" onClick={() => openModal('calendarEvent')}>{t('c.schedule')}</Btn></>} />
      <div className="flex wrap gap-8" style={{ marginBottom: 16 }}>
        <button style={chipStyle(filter === 'all')} onClick={() => setFilter('all')}>{t('task.cat.all')}</button>
        {APPT_CATEGORIES.map((c) => (
          <button key={c.key} style={chipStyle(filter === c.key)} onClick={() => setFilter(c.key)}>
            <Icon name={c.icon} size={13} />{t('appt.cat.' + c.key)}
          </button>
        ))}
      </div>
      {view === 'month' ? (
        <div className="dash-grid">
          <div className="col-8">
            <Card>
              <div className="cal-head">
                <div className="flex ac gap-10"><IconBtn name="chevron-left" /><div className="h3" style={{ minWidth: 150 }}>{dict.months[MONTH_IDX]} 2026</div><IconBtn name="chevron-right" /></div>
                <div className="grow" />
                <Btn variant="ghost" size="sm" icon="dot" onClick={() => setSel(TODAY)}>{t('c.today')}</Btn>
              </div>
              <div className="cal-grid">{dict.weekdays.map((w, i) => <div className="cal-dow" key={i}>{w}</div>)}</div>
              <div className="cal-grid">
                {cells.map((d, i) => {
                  if (d === null) return <div className="cal-cell empty" key={i} />;
                  const evs = evByDay[d] || [];
                  return (
                    <div key={i} className={'cal-cell ' + (d === TODAY ? 'today ' : '') + (d === sel ? 'sel' : '')} onClick={() => setSel(d)}>
                      <div className="cal-num">{d}</div>
                      {evs.map((e, j) => <div key={j} className={'cal-ev ' + e.tone}><span className="ev-dot" /><span className="ev-time mono">{e.time}</span> {titleOf(e).split(' — ')[0]}</div>)}
                    </div>
                  );
                })}
              </div>
            </Card>
          </div>
          <div className="col-4">
            <Card style={{ height: '100%' }}>
              <CardHead icon="calendar-clock" title={dow(sel) + ' · ' + sel + ' ' + dict.months[MONTH_IDX]} sub={selEvents.length + ' ' + (selEvents.length === 1 ? t('appt.event') : t('appt.events'))} />
              <div className="card-bd">
                {selEvents.length === 0
                  ? <div className="flex col ac jc" style={{ padding: '34px 10px', textAlign: 'center', gap: 10 }}><div className="hd-ic" style={{ width: 44, height: 44, background: 'var(--surface-3)', color: 'var(--ink-400)' }}><Icon name="calendar-x" size={20} /></div><div className="muted" style={{ fontSize: 13 }}>{t('appt.noEvents')}</div></div>
                  : <div className="flex col gap-12">{selEvents.map((e, i) => <ApptDetail key={i} e={e} title={titleOf(e)} loc={locOf(e)} dim={t('c.details')} />)}</div>}
              </div>
            </Card>
          </div>
          <div className="col-12">
            <Card>
              <CardHead icon="list" title={t('appt.upcoming')} sub={t('appt.thisMonth')} />
              <div className="card-bd" style={{ paddingTop: 4, paddingBottom: 4 }}>
                <div className="rowlist">
                  {rows.map((e, i) => (
                    <div className="lrow pointer" key={i} onClick={() => setSel(e.d)}>
                      <div className="l-ic" style={evTone(e.tone)}><Icon name={e.ic} size={18} /></div>
                      <div className="l-bd"><div className="l-t">{titleOf(e)}</div><div className="l-s">{dow(e.d)} {e.d} {dict.months[MONTH_IDX]} · {e.time} · {locOf(e)}</div></div>
                      <Btn variant="ghost" size="sm">{t('c.details')}</Btn>
                    </div>
                  ))}
                </div>
              </div>
            </Card>
          </div>
        </div>
      ) : (
        <Card><div className="card-bd"><div className="rowlist">
          {rows.map((e, i) => (
            <div className="lrow" key={i}>
              <div className="l-ic" style={evTone(e.tone)}><Icon name={e.ic} size={18} /></div>
              <div className="l-bd"><div className="l-t">{titleOf(e)}</div><div className="l-s">{dow(e.d)} {e.d} {dict.months[MONTH_IDX]} · {e.time} · {locOf(e)}</div></div>
              <Btn variant="ghost" size="sm">{t('c.details')}</Btn>
            </div>
          ))}
        </div></div></Card>
      )}
    </>
  );
}

/* ===== TASKS ===== */
const TASK_CATEGORIES = [
  { key: 'immigration', icon: 'shield', tone: 'info' },
  { key: 'document', icon: 'file-text', tone: 'neut' },
  { key: 'finance', icon: 'receipt', tone: 'warn' },
  { key: 'appointment', icon: 'calendar', tone: 'info' },
  { key: 'admin', icon: 'briefcase', tone: 'neut' },
  { key: 'other', icon: 'circle-dashed', tone: 'neut' },
];

function taskCatMeta(key) {
  return TASK_CATEGORIES.find((c) => c.key === key) || TASK_CATEGORIES[TASK_CATEGORIES.length - 1];
}

function TasksPage({ tasks, openModal, onSync, syncState, onToggleTask, contact, isAdmin, onAddSelfTask }) {
  const { t } = useT();
  const [filter, setFilter] = useState('all');
  const TICKET_TITLE_RE = /^\[[^\]]+\]\s*/;
  const rows = (tasks || [])
    .filter((tk) => !TICKET_TITLE_RE.test(String(tk.title || '')))
    .map((tk) => ({ ...tk, category: tk.category || 'other' }));
  const filtered = filter === 'all' ? rows : rows.filter((tk) => tk.category === filter);
  const chipStyle = (active) => ({
    display: 'inline-flex', alignItems: 'center', gap: 6, padding: '6px 11px', borderRadius: 999,
    border: '1px solid ' + (active ? 'var(--brand-500)' : 'var(--line)'),
    background: active ? 'var(--brand-50)' : 'var(--surface)', color: active ? 'var(--brand-700)' : 'var(--ink-700)',
    fontSize: 12.5, fontWeight: 600, cursor: 'pointer', whiteSpace: 'nowrap',
  });
  return (
    <>
      <PageHead title={t('nav.tasks')} sub={t('sub.tasks')}
        actions={isAdmin ? <Btn variant="ghost" icon="refresh-cw" onClick={onSync} disabled={syncState && syncState.syncing}>{syncState && syncState.syncing ? t('task.syncing') : t('task.syncGhl')}</Btn> : null} />
      {syncState && syncState.error && (
        <div className="alert warn" style={{ marginBottom: 14 }}>
          <div className="a-ic"><Icon name="alert-triangle" size={16} /></div>
          <div className="grow"><div className="a-t">{syncState.error}</div></div>
        </div>
      )}
      <div className="flex wrap gap-8" style={{ marginBottom: 16 }}>
        <button style={chipStyle(filter === 'all')} onClick={() => setFilter('all')}>{t('task.cat.all')}</button>
        {TASK_CATEGORIES.map((c) => (
          <button key={c.key} style={chipStyle(filter === c.key)} onClick={() => setFilter(c.key)}>
            <Icon name={c.icon} size={13} />{t('task.cat.' + c.key)}
          </button>
        ))}
      </div>
      <Card className="card-pad mb-16">
        <div className="flex ac gap-10 wrap">
          <Icon name="square-pen" size={17} className="dim" />
          <div className="grow" style={{ minWidth: 0, fontSize: 13, color: 'var(--ink-600)' }}>{t('task.ownPlaceholder')}</div>
          <Btn variant="primary" icon="plus" onClick={() => openModal && openModal('task')}>{t('task.addOwn')}</Btn>
        </div>
      </Card>
      <Card><div className="card-bd"><div className="rowlist">
        {filtered.length === 0 ? (
          <div className="lrow" style={{ borderBottom: 'none', justifyContent: 'center', padding: '24px 0', color: 'var(--ink-400)', fontSize: 13 }}>
            <Icon name="circle-check-big" size={16} />
            <span style={{ marginLeft: 8 }}>{t('task.empty')}</span>
          </div>
        ) : filtered.map((tk, i) => {
          const meta = taskCatMeta(tk.category);
          return (
            <div className="lrow" key={i}>
              <button className="icon-btn" onClick={() => onToggleTask && onToggleTask(tk)} style={{ width: 28, height: 28, color: tk.done ? 'var(--brand-500)' : 'var(--ink-300)' }}><Icon name={tk.done ? 'circle-check-big' : 'circle'} size={20} /></button>
              <div className="l-bd">
                <div className="l-t" style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', color: tk.done ? 'var(--ink-400)' : 'var(--ink-900)' }}>
                  {tk.taskType && <Badge tone={tk.taskTypeTone}>{t(tk.taskTypeLabelKey)}</Badge>}
                  <span style={{ textDecoration: tk.done ? 'line-through' : 'none' }}>{tk.title || t(tk.tk)}</span>
                </div>
                <div className="l-s" style={{ display: 'inline-flex', alignItems: 'center', gap: 5, marginTop: 2 }}>
                  <Icon name={meta.icon} size={12} />{t('task.cat.' + tk.category)}
                </div>
              </div>
              <div className="flex ac gap-8">
                <Badge tone={tk.tone} dot>{(tk.done ? t('c.done') : t('c.due')) + ' ' + tk.due}</Badge>
                {tk.taskType !== 'bilgi' && <Btn variant="ghost" size="sm" icon="paperclip" onClick={(e) => { e.stopPropagation(); openModal("taskAttachment", { task: tk, contact }); }}>{t("task.attach.btn")}</Btn>}
              </div>
            </div>
          );
        })}
      </div></div></Card>
    </>
  );
}

/* ===== REPORTS ===== */
function MiniBars() {
  const data = [40, 55, 48, 62, 70, 58, 75, 68, 82, 78, 90, 85];
  return (
    <div style={{ display: 'flex', alignItems: 'flex-end', gap: 8, height: 96, padding: '0 2px' }}>
      {data.map((h, i) => (
        <div key={i} className="grow" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6 }}>
          <div style={{ width: '100%', height: h + '%', borderRadius: 6, background: i === data.length - 1 ? 'var(--brand-500)' : 'var(--brand-100)' }} />
          <span className="dim" style={{ fontSize: 9.5 }}>{i + 1}</span>
        </div>
      ))}
    </div>
  );
}
function ReportsPage() {
  const { t } = useT();
  return (
    <>
      <PageHead title={t('nav.reports')} sub={t('sub.reports')} actions={<Btn variant="ghost" icon="download">{t('c.exportPdf')}</Btn>} />
      <div className="grid g-12 mb-20" style={{ gap: 16 }}>
        <div className="col-3"><Stat icon="folder-check" value="92%" label={t('rep.completion')} trend="+8%" /></div>
        <div className="col-3"><Stat icon="clock" tone="blue" value="34d" label={t('rep.processing')} /></div>
        <div className="col-3"><Stat icon="shield-check" value="100%" label={t('rep.compliance')} /></div>
        <div className="col-3"><Stat icon="file-check-2" value="18" label={t('rep.docs')} /></div>
      </div>
      <Card><CardHead icon="chart-line" title={t('rep.activity')} sub={t('rep.last12')} /><div className="card-bd"><MiniBars /></div></Card>
    </>
  );
}

function SimplePage({ icon, title, sub }) {
  return (
    <div style={{ maxWidth: 520, margin: '8vh auto', textAlign: 'center' }}>
      <div className="hd-ic" style={{ width: 60, height: 60, margin: '0 auto 18px', borderRadius: 16 }}><Icon name={icon} size={28} /></div>
      <h1 className="h1" style={{ marginBottom: 8 }}>{title}</h1>
      <p className="muted">{sub}</p>
    </div>
  );
}

function ProfilePage({ contact, onRefresh }) {
  const { t } = useT();
  const [wizardOpen, setWizardOpen] = useState(false);
  const [wizardSection, setWizardSection] = useState(null);
  const authState = (window.YEYE_AUTH && window.YEYE_AUTH.getState && window.YEYE_AUTH.getState()) || {};

  if (!contact) {
    return (
      <>
        <PageHead title={t('nav.profile')} sub={t('pg.profileSub')} />
        <Card className="profile-loading-card">
          <div className="card-bd">
            <div className="profile-loading-dot" aria-hidden="true" />
            <span>{t('profileFields.loading')}</span>
          </div>
        </Card>
      </>
    );
  }

  const user = authState.user || {};
  const name = contactDisplayName(contact) || user.name || t('nav.profile');
  const email = contact.email || user.email || '';
  const initials = name.split(/\s+/).filter(Boolean).map((word) => word[0]).join('').slice(0, 2).toUpperCase();
  const customAvatar = window.YEYE_AUTH && window.YEYE_AUTH.getAvatar && window.YEYE_AUTH.getAvatar();
  const avatar = customAvatar && customAvatar.type === 'emoji'
    ? <div className="profile-page-avatar profile-page-avatar-emoji">{customAvatar.value}</div>
    : customAvatar && customAvatar.type === 'image'
      ? <img className="profile-page-avatar" src={customAvatar.value} alt="" />
      : user.picture
        ? <img className="profile-page-avatar" src={user.picture} alt="" />
        : <Avatar size="lg" tone={{ bg: 'var(--brand-500)', fg: '#fff' }}>{initials}</Avatar>;

  return (
    <>
      <PageHead title={t('nav.profile')} sub={t('pg.profileSub')} />
      <Card className="profile-page-head mb-18">
        <div className="card-bd">
          {avatar}
          <div>
            <h2>{name}</h2>
            {email && <p>{email}</p>}
          </div>
        </div>
      </Card>
      <Card>
        <div className="card-bd">
          <RequiredFieldsGrid
            contact={contact}
            title={t('profileFields.essentials')}
            onEdit={(entry) => {
              setWizardSection(entry.section);
              setWizardOpen(true);
            }}
          />
        </div>
      </Card>
      {wizardOpen && (
        <ProfileWizard
          contact={contact}
          initialSection={wizardSection}
          onClose={() => setWizardOpen(false)}
          onSaved={() => {
            setWizardOpen(false);
            if (onRefresh) onRefresh();
          }}
        />
      )}
    </>
  );
}

const AVATAR_EMOJIS = ['🙂', '😎', '🤩', '🥳', '🦸', '🧑‍💻', '🐱', '🐶', '🦊', '🐼', '🐨', '🐯', '🦁', '🌟', '⚡', '🚀', '🎯', '🌈', '🌵', '🍀', '☕', '🎨', '📚', '💼'];

function AvatarSection() {
  const { t } = useT();
  const [avatar, setAvatarState] = useState(() => (window.YEYE_AUTH.getAvatar && window.YEYE_AUTH.getAvatar()) || null);
  const [uploadErr, setUploadErr] = useState(null);

  const apply = (av) => { window.YEYE_AUTH.setAvatar(av); setAvatarState(av); setUploadErr(null); };

  const onFile = (e) => {
    const file = e.target.files && e.target.files[0];
    if (!file) return;
    if (file.size > 400 * 1024) { setUploadErr(t('set.avatarTooLarge')); return; }
    const reader = new FileReader();
    reader.onload = () => apply({ type: 'image', value: reader.result });
    reader.onerror = () => setUploadErr(t('set.avatarReadFailed'));
    reader.readAsDataURL(file);
    e.target.value = '';
  };

  const preview = avatar && avatar.type === 'emoji'
    ? <div className="avatar a-lg" style={{ background: 'var(--brand-100)', color: 'var(--ink-900)', fontSize: 26, width: 60, height: 60 }}>{avatar.value}</div>
    : avatar && avatar.type === 'image'
      ? <img src={avatar.value} alt="" className="avatar a-lg" style={{ width: 60, height: 60, borderRadius: '50%', objectFit: 'cover' }} />
      : <Avatar size="lg" tone={{ bg: 'var(--brand-500)', fg: '#fff' }}>?</Avatar>;

  return (
    <Card>
      <CardHead icon="user" title={t('set.avatar')} sub={t('set.avatarSub')} />
      <div className="card-bd">
        <div className="flex ac gap-16" style={{ marginBottom: 18 }}>
          {preview}
          <div className="flex col gap-6">
            <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink-900)' }}>{t('set.avatarCurrent')}</div>
            {avatar && <button className="btn btn-ghost btn-sm" onClick={() => apply(null)} style={{ alignSelf: 'flex-start' }}>{t('set.avatarRemove')}</button>}
          </div>
        </div>
        <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-700)', marginBottom: 10 }}>{t('set.avatarPickEmoji')}</div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(48px, 1fr))', gap: 8, marginBottom: 18 }}>
          {AVATAR_EMOJIS.map((emo) => (
            <button key={emo} type="button" onClick={() => apply({ type: 'emoji', value: emo })}
              style={{
                aspectRatio: '1', border: '1px solid var(--line)', borderRadius: 12, background: avatar && avatar.value === emo ? 'var(--brand-100)' : 'var(--surface)',
                fontSize: 24, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
              }}>{emo}</button>
          ))}
        </div>
        <div style={{ fontSize: 12, fontWeight: 700, color: 'var(--ink-700)', marginBottom: 8 }}>{t('set.avatarUpload')}</div>
        <label className="btn btn-soft btn-sm" style={{ cursor: 'pointer', display: 'inline-flex' }}>
          <Icon name="upload" size={14} />{t('set.avatarUploadBtn')}
          <input type="file" accept="image/*" onChange={onFile} style={{ display: 'none' }} />
        </label>
        {uploadErr && <div className="alert bad" style={{ marginTop: 12 }}><div className="a-ic"><Icon name="alert-triangle" size={16} /></div><div className="grow"><div className="a-t">{uploadErr}</div></div></div>}
        <div className="dim" style={{ fontSize: 11, marginTop: 8 }}>{t('set.avatarUploadHint')}</div>
      </div>
    </Card>
  );
}

function CurrencySection() {
  const { t, currency, setCurrency } = useT();
  const options = [
    { k: 'EUR', label: 'EUR', sub: '€' },
    { k: 'CZK', label: 'CZK', sub: 'Kč' },
    { k: 'TRY', label: 'TL', sub: '₺' },
  ];
  return (
    <Card>
      <CardHead icon="coins" title={t('currency.title')} sub={t('currency.sub')} />
      <div className="card-bd">
        <div className="flex gap-8 wrap">
          {options.map((opt) => (
            <Btn key={opt.k} variant={currency === opt.k ? 'primary' : 'ghost'} icon={currency === opt.k ? 'check' : 'coins'} onClick={() => setCurrency(opt.k)}>
              {opt.label} <span className="mono" style={{ opacity: 0.75 }}>{opt.sub}</span>
            </Btn>
          ))}
        </div>
        <div className="muted" style={{ fontSize: 12.5, marginTop: 10 }}>{t('currency.rateNote')}</div>
      </div>
    </Card>
  );
}

function SettingsPage() {
  const { t } = useT();
  const [current, setCurrent] = useState('');
  const [next, setNext] = useState('');
  const [confirm, setConfirm] = useState('');
  const [status, setStatus] = useState(null);
  const [busy, setBusy] = useState(false);
  const [deleteOpen, setDeleteOpen] = useState(false);
  const [deletePassword, setDeletePassword] = useState('');
  const [deleteStatus, setDeleteStatus] = useState(null);
  const [deleteBusy, setDeleteBusy] = useState(false);

  const errText = (code) => {
    switch (code) {
      case 'wrong-current': return t('set.pwWrongCurrent');
      case 'too-short': return t('set.pwTooShort');
      case 'same-as-current': return t('set.pwSameAsCurrent');
      case 'mismatch': return t('set.pwMismatch');
      case 'not-signed-in': return t('set.pwNotSignedIn');
      default: return t('set.pwGeneric');
    }
  };

  const submit = async (e) => {
    e.preventDefault();
    setStatus(null);
    if (next !== confirm) { setStatus({ ok: false, code: 'mismatch' }); return; }
    setBusy(true);
    try {
      const res = await window.YEYE_AUTH.changePassword(current, next);
      if (res && res.ok) {
        setStatus({ ok: true });
        setCurrent(''); setNext(''); setConfirm('');
      } else {
        setStatus({ ok: false, code: (res && res.code) || 'unknown' });
      }
    } catch (err) {
      setStatus({ ok: false, code: 'unknown' });
    } finally {
      setBusy(false);
    }
  };

  const fieldStyle = {
    height: 44, borderRadius: 10, border: '1px solid var(--line)',
    padding: '0 12px', fontSize: 14, background: 'var(--surface)',
    color: 'var(--ink-900)', outline: 'none',
  };

  const deleteErrText = (code) => {
    switch (code) {
      case 'wrong-current': return t('set.deleteWrongPassword');
      case 'mail-failed': return t('set.deleteMailFailed');
      case 'not-found': return t('set.deleteNotFound');
      case 'unauthorized': return t('set.deleteUnauthorized');
      case 'docs-failed': return t('set.deleteDocsFailed');
      case 'dnd-failed': return t('set.deleteDndFailed');
      case 'firestore-failed': return t('set.deleteFirestoreFailed');
      case 'auth-delete-failed': return t('set.deleteAuthFailed');
      default: return t('set.deleteGeneric');
    }
  };

  const submitDelete = async (e) => {
    e.preventDefault();
    setDeleteStatus(null);
    setDeleteBusy(true);
    try {
      const result = await window.YEYE_AUTH.deleteAccount(deletePassword);
      if (result && result.ok) {
        // Wipe every client cache so the deleted account can never re-appear from
        // a stale localStorage/session (the "still visible on mobile" bug), then
        // force a hard reload to a clean login state.
        try { localStorage.clear(); } catch (_) {}
        try { sessionStorage.clear(); } catch (_) {}
        window.location.replace('/?account-deleted=1');
        return;
      }
      setDeleteStatus({ ok: false, code: (result && result.code) || 'unknown' });
    } catch (_) {
      setDeleteStatus({ ok: false, code: 'unknown' });
    } finally {
      setDeleteBusy(false);
    }
  };

  return (
    <>
      <PageHead title={t('nav.settings')} sub={t('pg.settingsSub')} />
      <div style={{ maxWidth: 520, display: 'flex', flexDirection: 'column', gap: 20 }}>
        <AvatarSection />
        <CurrencySection />
        <Card>
          <CardHead icon="lock" title={t('set.changePassword')} sub={t('set.changePasswordSub')} />
          <div className="card-bd">
            <form onSubmit={submit} style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
              <label style={{ display: 'flex', flexDirection: 'column', gap: 6, fontSize: 12, fontWeight: 700, color: 'var(--ink-700)' }}>
                {t('set.currentPassword')}
                <input type="password" value={current} onChange={(e) => setCurrent(e.target.value)} required autoComplete="current-password" style={fieldStyle} />
              </label>
              <label style={{ display: 'flex', flexDirection: 'column', gap: 6, fontSize: 12, fontWeight: 700, color: 'var(--ink-700)' }}>
                {t('set.newPassword')}
                <input type="password" value={next} onChange={(e) => setNext(e.target.value)} required minLength={6} autoComplete="new-password" style={fieldStyle} />
              </label>
              <label style={{ display: 'flex', flexDirection: 'column', gap: 6, fontSize: 12, fontWeight: 700, color: 'var(--ink-700)' }}>
                {t('set.confirmPassword')}
                <input type="password" value={confirm} onChange={(e) => setConfirm(e.target.value)} required minLength={6} autoComplete="new-password" style={fieldStyle} />
              </label>
              {status && !status.ok && (
                <div className="alert bad" style={{ margin: 0 }}>
                  <div className="a-ic"><Icon name="alert-triangle" size={16} /></div>
                  <div className="grow"><div className="a-t">{errText(status.code)}</div></div>
                </div>
              )}
              {status && status.ok && (
                <div className="alert ok" style={{ margin: 0 }}>
                  <div className="a-ic"><Icon name="check" size={16} /></div>
                  <div className="grow"><div className="a-t">{t('set.pwUpdated')}</div></div>
                </div>
              )}
              <div>
                <Btn variant="primary" type="submit" disabled={busy}>{busy ? t('c.saving') : t('set.savePassword')}</Btn>
              </div>
            </form>
          </div>
        </Card>
        <Card className="danger-zone">
          <CardHead icon="triangle-alert" title={t('set.dangerZone')} sub={t('set.dangerZoneSub')} />
          <div className="card-bd">
            <Btn variant="danger" icon="trash-2" onClick={() => { setDeletePassword(''); setDeleteStatus(null); setDeleteOpen(true); }}>
              {t('set.deleteAccount')}
            </Btn>
          </div>
        </Card>
      </div>
      {deleteOpen && (
        <Modal
          title={t('set.deleteTitle')}
          icon="triangle-alert"
          onClose={() => { if (!deleteBusy) setDeleteOpen(false); }}
          footer={(
            <>
              <Btn type="button" onClick={() => setDeleteOpen(false)} disabled={deleteBusy}>{t('set.deleteCancel')}</Btn>
              <Btn variant="danger" type="submit" form="delete-account-form" disabled={deleteBusy || !deletePassword}>
                {deleteBusy ? t('set.deleting') : t('set.deleteConfirm')}
              </Btn>
            </>
          )}
        >
          <form id="delete-account-form" onSubmit={submitDelete} style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
            <div className="delete-account-warning">
              <Icon name="octagon-alert" size={20} />
              <div>{t('set.deleteWarning')}</div>
            </div>
            <label style={{ display: 'flex', flexDirection: 'column', gap: 6, fontSize: 12, fontWeight: 700, color: 'var(--ink-700)' }}>
              {t('set.deletePassword')}
              <input
                type="password"
                value={deletePassword}
                onChange={(e) => setDeletePassword(e.target.value)}
                required
                autoComplete="current-password"
                style={fieldStyle}
              />
            </label>
            {deleteStatus && !deleteStatus.ok && (
              <div className="alert bad" style={{ margin: 0 }}>
                <div className="a-ic"><Icon name="alert-triangle" size={16} /></div>
                <div className="grow"><div className="a-t">{deleteErrText(deleteStatus.code)}</div></div>
              </div>
            )}
          </form>
        </Modal>
      )}
    </>
  );
}

Object.assign(window, {
  ServicesPage, PurchasedServicesPage, DocumentsPage, MessagesPage, InvoicesPage, SupportPage,
  FormSubmissionPage, AppointmentsPage, TasksPage, ReportsPage, SimplePage, ProfilePage, SettingsPage, AvatarSection,
});
