/* OfficeTrack — chat UI components (lean set for the support chat).
   Exported on `window` for the app scripts to consume. */

const NCColors = {
  lavender: '#9F95E3', lavenderLight: '#F4F1FB', lavender200: '#CFC8F0',
  navy: '#15296B', navyDark: '#0E1F55', navy900: '#07112E',
  yellow: '#F5A623', yellowSoft: '#FFD37A', blue: '#2E7BC4', cobalt: '#1A4FE8',
  white: '#FFFFFF',
  gray25: '#FAFAFB', gray50: '#F4F4F6', gray100: '#ECECEF', gray200: '#DCDCE2',
  gray300: '#C4C4CC', gray400: '#9A9AA5', gray500: '#707079', gray600: '#4A4A52',
  gray700: '#2E2E36', gray900: '#0F0F14',
  success: '#5DB85D', danger: '#DA5350', warning: '#F0AD4E',
};

/* ───────── OfficeTrack duotone icons (lavender fill + navy stroke + yellow) ───────── */
const OTIcons = {
  clock: (
    <svg viewBox="0 0 32 32" width="22" height="22" aria-hidden="true">
      <circle cx="16" cy="16" r="12" fill={NCColors.lavender200} stroke={NCColors.navy} strokeWidth="2.6"/>
      <path d="M16 9v7l5 3" fill="none" stroke={NCColors.yellow} strokeWidth="2.6" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  ),
  pin: (
    <svg viewBox="0 0 32 32" width="20" height="20" aria-hidden="true">
      <path d="M16 4c-4.4 0-8 3.6-8 8 0 6 8 16 8 16s8-10 8-16c0-4.4-3.6-8-8-8z"
            fill={NCColors.lavender200} stroke={NCColors.navy} strokeWidth="2.4" strokeLinejoin="round"/>
      <circle cx="16" cy="12" r="3.2" fill={NCColors.yellow}/>
    </svg>
  ),
};

/* ───────── header (brand + language toggle + reset) ───────── */
function ChatHeader({ t, lang, onLang, brandColor, brandName, onReset }) {
  return (
    <div className="nc-header" style={{ background: brandColor }}>
      <div className="nc-header-left">
        <div className="nc-logo-mark">
          <svg viewBox="0 0 34 24" width="27" height="19">
            <text x="2" y="18" fontFamily="Inter, system-ui, sans-serif" fontWeight="800" fontSize="19" letterSpacing="-0.5">
              <tspan fill={NCColors.navy}>O</tspan><tspan fill={NCColors.yellow}>T</tspan>
            </text>
          </svg>
        </div>
        <div className="nc-header-titles">
          <div className="nc-header-brand">{brandName}</div>
          <div className="nc-header-status">
            <span className="nc-online-dot"/>{t.online}
          </div>
        </div>
      </div>
      <div className="nc-header-actions">
        {onLang && (
          <div className="nc-lang-toggle" role="group" aria-label="language">
            {['es', 'en', 'pt', 'he'].map(code => (
              <button key={code}
                      className={'nc-lang-btn' + (code === lang ? ' active' : '')}
                      onClick={() => code !== lang && onLang(code)}>
                {code.toUpperCase()}
              </button>
            ))}
          </div>
        )}
        {onReset && (
          <button className="nc-header-reset" onClick={onReset}
                  title={t.resetTitle || 'Start over'} aria-label="Start over">
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none"
                 stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
              <polyline points="1 4 1 10 7 10"/>
              <path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"/>
            </svg>
          </button>
        )}
      </div>
    </div>
  );
}

/* ───────── text cleanup + lightweight markdown ─────────
   The LLM sometimes emits empty ```json fences or markdown like **bold** /
   bullet lists. Strip empty fences and render a minimal markdown subset. */
function cleanAiText(s) {
  if (!s) return '';
  s = s.replace(/```[a-zA-Z0-9_-]*\s*```/g, '');
  s = s.replace(/\n{3,}/g, '\n\n');
  return s.trim();
}

function renderInline(s) {
  const parts = [];
  const re = /\*\*(.+?)\*\*/g;
  let i = 0, m;
  while ((m = re.exec(s)) !== null) {
    if (m.index > i) parts.push(s.slice(i, m.index));
    parts.push(<strong key={parts.length}>{m[1]}</strong>);
    i = re.lastIndex;
  }
  if (i < s.length) parts.push(s.slice(i));
  return parts;
}

function RichText({ text }) {
  if (!text) return null;
  const lines = text.split('\n');
  const blocks = [];
  let listBuf = [];
  const flush = () => {
    if (listBuf.length) {
      blocks.push(
        <ul key={'l' + blocks.length} className="nc-rt-ul">
          {listBuf.map((it, i) => <li key={i}>{renderInline(it)}</li>)}
        </ul>);
      listBuf = [];
    }
  };
  for (const raw of lines) {
    const line = raw.trim();
    if (!line) { flush(); continue; }
    const m = line.match(/^[-•*]\s+(.+)/);
    if (m) { listBuf.push(m[1]); }
    else { flush(); blocks.push(<p key={'p' + blocks.length} className="nc-rt-p">{renderInline(line)}</p>); }
  }
  flush();
  return <>{blocks}</>;
}

/* ───────── chat bubble ───────── */
function ChatMsg({ from, children, brandColor }) {
  return (
    <div className={'nc-msg nc-msg-' + from}>
      {from === 'ai' && (
        <div className="nc-msg-avatar" style={{ background: brandColor }}>
          <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#fff" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
            <path d="M12 3 L21 8 V16 L12 21 L3 16 V8 Z"/>
            <circle cx="12" cy="12" r="3" fill="#fff"/>
          </svg>
        </div>
      )}
      <div className={'nc-bubble nc-bubble-' + from}
           style={from === 'me' ? { background: brandColor, color: '#fff' } : null}>
        {children}
      </div>
    </div>
  );
}

/* ───────── typing dots ───────── */
function TypingDots({ brandColor }) {
  return (
    <div className="nc-msg nc-msg-ai">
      <div className="nc-msg-avatar" style={{ background: brandColor }}>
        <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="#fff" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
          <path d="M12 3 L21 8 V16 L12 21 L3 16 V8 Z"/><circle cx="12" cy="12" r="3" fill="#fff"/>
        </svg>
      </div>
      <div className="nc-bubble nc-bubble-ai nc-typing"><span/><span/><span/></div>
    </div>
  );
}

/* ───────── suggestion chips (used by the multi-account picker) ───────── */
function SuggestionChips({ items, onPick, brandColor }) {
  return (
    <div className="nc-suggestions">
      {items.map((it, i) => (
        <button key={i} className="nc-chip" onClick={() => onPick(it)}>
          {it.icon && <span className="nc-chip-icon" style={{ color: brandColor }}>{it.icon}</span>}
          <span>{it.label}</span>
        </button>
      ))}
    </div>
  );
}

/* ───────── composer (text + photo upload + voice) ───────── */
function Composer({ t, value, onChange, onSend, brandColor, disabled,
                    allowImage, awaitingImage, pendingImage,
                    onPickImage, onClearImage,
                    voiceSupported, listening, onVoice }) {
  const ref = React.useRef(null);
  const fileRef = React.useRef(null);
  const canSend = !disabled && (value.trim() || pendingImage);
  const attachHot = awaitingImage && !pendingImage;
  const showMic = voiceSupported && allowImage && !value.trim() && !pendingImage;
  return (
    <div className={'nc-composer-wrap' + (disabled ? ' nc-composer-disabled' : '')}>
      {pendingImage && (
        <div className="nc-photo-preview">
          <img src={pendingImage} alt="" />
          <span className="nc-photo-preview-label">{t.photoSelected}</span>
          <button className="nc-photo-preview-x" onClick={onClearImage}
                  aria-label={t.photoRemove}>×</button>
        </div>
      )}
      <div className="nc-composer">
        {allowImage && (
          <>
            <input ref={fileRef} type="file" accept="image/*" capture="environment"
                   style={{ display: 'none' }}
                   onChange={e => {
                     const f = e.target.files && e.target.files[0];
                     if (f && onPickImage) onPickImage(f);
                     e.target.value = '';
                   }}/>
            <button className={'nc-comp-icon' + (attachHot ? ' nc-comp-icon-hot' : '')}
                    aria-label={t.attachLabel} disabled={disabled}
                    title={t.attachLabel}
                    onClick={() => fileRef.current && fileRef.current.click()}
                    style={attachHot ? { color: brandColor } : null}>
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none"
                   stroke={attachHot ? brandColor : NCColors.gray500}
                   strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
                <path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"/>
                <circle cx="12" cy="13" r="4"/>
              </svg>
            </button>
          </>
        )}
        <div className="nc-comp-input">
          <input ref={ref} value={value} disabled={disabled}
                 onChange={e => onChange(e.target.value)}
                 onKeyDown={e => { if (e.key === 'Enter' && canSend) onSend(); }}
                 placeholder={t.chatPlaceholder}/>
        </div>
        {showMic ? (
          <button className={'nc-comp-mic' + (listening ? ' nc-comp-mic-on' : '')}
                  onClick={() => !disabled && onVoice && onVoice()}
                  disabled={disabled}
                  aria-label={t.voiceLabel}
                  title={listening ? t.voiceListening : t.voiceLabel}
                  style={listening ? { background: brandColor, color: '#fff' } : { color: brandColor }}>
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none"
                 stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
              <rect x="9" y="2" width="6" height="13" rx="3"/>
              <path d="M5 11a7 7 0 0014 0"/>
              <line x1="12" y1="19" x2="12" y2="23"/>
              <line x1="8" y1="23" x2="16" y2="23"/>
            </svg>
          </button>
        ) : (
          <button className="nc-comp-send" onClick={() => canSend && onSend()}
                  disabled={!canSend}
                  style={{ background: brandColor, opacity: canSend ? 1 : 0.4 }}
                  aria-label={t.sendLabel}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
              <line x1="22" y1="2" x2="11" y2="13"/>
              <polygon points="22 2 15 22 11 13 2 9 22 2"/>
            </svg>
          </button>
        )}
      </div>
      {listening && (
        <div style={{ padding: '0 14px 8px', fontSize: 12, color: brandColor,
                      fontWeight: 600 }}>{t.voiceListening}</div>
      )}
    </div>
  );
}

/* ───────── slot chips (available times → tappable cards) ───────── */
function SlotChips({ slots, onPick, disabled }) {
  return (
    <div className="nc-slotchips">
      {slots.map((s, i) => (
        <button key={i} className="nc-slotchip" disabled={disabled}
                onClick={() => !disabled && onPick(s)}>
          <span className="nc-slotchip-ic">{OTIcons.clock}</span>
          <span className="nc-slotchip-txt">
            <span className="nc-slotchip-date">{s.label}</span>
            {s.time && <span className="nc-slotchip-time">{s.time}</span>}
          </span>
        </button>
      ))}
    </div>
  );
}

/* ───────── location sheet (Leaflet map, confirm before booking) ─────────
   Bottom-sheet with an interactive OSM map: detect the customer's position,
   let them drag the pin, reverse-geocode the address, and confirm. */
function LocationSheet({ t, open, brandColor, onConfirm, onCancel }) {
  const mapRef = React.useRef(null);
  const mapObj = React.useRef(null);
  const markerRef = React.useRef(null);
  const geoTimer = React.useRef(null);
  const [coords, setCoords] = React.useState(null);
  const [address, setAddress] = React.useState('');
  const [loadingAddr, setLoadingAddr] = React.useState(false);

  function reverseGeocode(lat, lng, lang) {
    setLoadingAddr(true);
    clearTimeout(geoTimer.current);
    geoTimer.current = setTimeout(() => {
      const url = 'https://nominatim.openstreetmap.org/reverse?format=jsonv2'
        + `&lat=${lat}&lon=${lng}&accept-language=${lang || 'es'}`;
      fetch(url, { headers: { 'Accept': 'application/json' } })
        .then(r => r.json())
        .then(d => { setAddress((d && d.display_name) || ''); setLoadingAddr(false); })
        .catch(() => setLoadingAddr(false));
    }, 600);
  }

  React.useEffect(() => {
    if (!open) return;
    const L = window.L;
    if (!L || !mapRef.current) return;
    const lang = (t && t.code ? t.code.toLowerCase() : 'es');
    const start = [-34.6037, -58.3816]; // neutral fallback until we locate
    const map = L.map(mapRef.current, { zoomControl: true }).setView(start, 13);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
      { maxZoom: 19, attribution: '© OpenStreetMap' }).addTo(map);
    // OfficeTrack duotone pin (lavender fill + navy stroke + yellow dot).
    const pinHtml =
      '<svg width="38" height="48" viewBox="0 0 38 48" xmlns="http://www.w3.org/2000/svg">'
      + '<path d="M19 2C11 2 5 8 5 16c0 10 14 30 14 30s14-20 14-30C33 8 27 2 19 2z" '
      + 'fill="#CFC8F0" stroke="#15296B" stroke-width="2.6" stroke-linejoin="round"/>'
      + '<circle cx="19" cy="16" r="5" fill="#F5A623"/></svg>';
    const otIcon = L.divIcon({
      className: 'nc-map-pin', html: pinHtml,
      iconSize: [38, 48], iconAnchor: [19, 46],
    });
    const marker = L.marker(start, { draggable: true, icon: otIcon }).addTo(map);
    mapObj.current = map; markerRef.current = marker;
    const set = (lat, lng) => { setCoords({ lat, lng }); reverseGeocode(lat, lng, lang); };
    marker.on('dragend', () => { const p = marker.getLatLng(); map.panTo(p); set(p.lat, p.lng); });
    map.on('click', (e) => { marker.setLatLng(e.latlng); set(e.latlng.lat, e.latlng.lng); });
    set(start[0], start[1]);
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition((pos) => {
        const { latitude, longitude } = pos.coords;
        map.setView([latitude, longitude], 16);
        marker.setLatLng([latitude, longitude]);
        set(latitude, longitude);
      }, () => {}, { enableHighAccuracy: true, timeout: 10000, maximumAge: 60000 });
    }
    setTimeout(() => map.invalidateSize(), 250);
    return () => { clearTimeout(geoTimer.current); map.remove(); mapObj.current = null; markerRef.current = null; };
  }, [open]);

  if (!open) return null;
  return (
    <div className="nc-sheet-scrim" onClick={onCancel}>
      <div className="nc-sheet" onClick={e => e.stopPropagation()}>
        <div className="nc-sheet-title">{t.mapTitle}</div>
        <div ref={mapRef} className="nc-map"></div>
        <div className="nc-map-addr">
          {loadingAddr
            ? t.locating
            : (address ? <span><b>{t.isItHere}</b> {address}</span> : t.dragHint)}
        </div>
        <div className="nc-sheet-actions">
          <button className="nc-btn nc-btn-secondary" onClick={onCancel}>{t.mapCancel}</button>
          <button className="nc-btn nc-btn-primary" style={{ background: brandColor }}
                  disabled={!coords}
                  onClick={() => coords && onConfirm({ ...coords, address })}>
            {t.useThisLocation}
          </button>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, {
  NCColors, OTIcons, ChatHeader, ChatMsg, TypingDots, SuggestionChips,
  SlotChips, Composer, LocationSheet, cleanAiText, RichText,
});
