// === Question type components ===
// Each component takes (question, value, onChange, context) and renders UI

const { useState, useEffect, useRef, useMemo } = React;

// ---- Utility: call AI (OpenAI via aiCache / aiComplete) ----
async function generateExamples(question, context, previous = []) {
  if (!question.aiPrompt) return question.fallback || question.options || [];

  // Try cache-backed generator first (no `previous` variation via cache; if `previous` is non-empty,
  // fall back to a fresh call bypassing cache to allow regenerate with exclusions)
  if (window.aiCache && (!previous || previous.length === 0)) {
    try {
      const data = await window.aiCache.generateForQuestion(question, context);
      return data && data.length > 0 ? data : (question.fallback || []);
    } catch (err) {
      console.warn('aiCache generation failed:', err);
    }
  }

  // Fresh call with exclusions (used when user clicks "regenerate")
  try {
    const contextSummary = window.aiCache?.buildContextSummary?.(context) || buildContextSummary(context);
    const exclusionNote = previous && previous.length
      ? `\n\nYA MOSTRADAS (NO REPITAS ni uses variaciones cercanas, genera propuestas nuevas y distintas):\n${previous.map(p => '- ' + p).join('\n')}`
      : '';
    const prompt = `Contexto del negocio:\n${contextSummary}\n\nTarea: ${question.aiPrompt}${exclusionNote}\n\nResponde SOLO con un JSON array válido envuelto en un objeto con la clave "items".`;

    if (window.aiComplete) {
      const res = await window.aiComplete(prompt, { jsonMode: true, temperature: 0.8 }, question.id);
      if (res.ok) {
        try {
          const parsed = JSON.parse(res.data);
          const data = Array.isArray(parsed) ? parsed : (parsed.items || parsed.options || parsed.data || null);
          if (data && data.length > 0) return data;
        } catch {
          const arr = window.aiCache?.parseJsonArray?.(res.data);
          if (arr) return arr;
        }
      }
    }
    return question.fallback || [];
  } catch (err) {
    console.warn('AI generation failed, using fallback:', err);
    return question.fallback || [];
  }
}

function buildContextSummary(answers) {
  const lines = [];
  if (answers.business_name) lines.push(`Negocio: ${answers.business_name}`);
  if (answers.business_description) lines.push(`Descripción: ${answers.business_description}`);
  if (answers.business_differentiator) lines.push(`Diferenciador: ${answers.business_differentiator}`);
  // Tono y reglas de estilo (sección 4) — los textos generados después
  // (saludos, pitches) deben respetarlos.
  if (answers.tone?.sample) lines.push(`Tono elegido (ejemplo de referencia): ${answers.tone.sample}`);
  const ws = answers.writing_style;
  if (ws && typeof ws === 'object') {
    const wsMeta = (window.QUESTIONNAIRE || []).find(q => q.id === 'writing_style');
    const rules = [];
    for (const t of wsMeta?.toggles || []) {
      const side = ws[t.id];
      if (side === 'left' || side === 'right') rules.push(`${t.title}: ${side === 'left' ? t.left : t.right}`);
    }
    if (rules.length) lines.push(`Reglas de estilo (OBLIGATORIAS en cualquier texto generado): ${rules.join(' · ')}`);
  }
  if (answers.ideal_customer?.sample) lines.push(`Cliente ideal: ${answers.ideal_customer.sample}`);
  if (answers.customer_problem) {
    const cp = Array.isArray(answers.customer_problem)
      ? answers.customer_problem.map(x => x.label || x).join(', ')
      : (answers.customer_problem.label || answers.customer_problem);
    if (cp) lines.push(`Problema principal: ${cp}`);
  }
  if (Array.isArray(answers.agent_types) && answers.agent_types.length) {
    lines.push(`Tipos de agente: ${answers.agent_types.join(', ')}`);
  }
  if (Array.isArray(answers.agendador_prequal_questions) && answers.agendador_prequal_questions.length) {
    const qs = answers.agendador_prequal_questions.map(x => x.q || x).filter(Boolean).join(' | ');
    if (qs) lines.push(`Preguntas de pre-calificación: ${qs}`);
  }
  return lines.join('\n') || 'Aún sin descripción de negocio.';
}

// ---- TEXT (single-line) ----
function TextInput({ question, value, onChange, onEnter }) {
  const ref = useRef();
  const [touched, setTouched] = useState(false);
  useEffect(() => { ref.current?.focus(); }, []);
  const inputType = question.inputType || 'text';
  const autoComplete =
    inputType === 'email' ? 'email'
    : inputType === 'tel' ? 'tel'
    : inputType === 'url' ? 'url'
    : 'off';
  const invalid = touched && value && question.validator && !question.validator(value);
  return (
    <div>
      <input
        ref={ref}
        className={`textfield ${invalid ? 'is-invalid' : ''}`}
        type={inputType}
        inputMode={inputType === 'tel' ? 'tel' : inputType === 'email' ? 'email' : 'text'}
        autoComplete={autoComplete}
        value={value || ''}
        placeholder={question.placeholder || 'Escribe aquí...'}
        onChange={(e) => onChange(e.target.value)}
        onBlur={() => setTouched(true)}
        onKeyDown={(e) => {
          if (e.key === 'Enter' && onEnter) { e.preventDefault(); onEnter(); }
        }}
      />
      {invalid && <div className="textfield-error">{question.errorMessage || 'Formato inválido'}</div>}
    </div>
  );
}

// ---- TEXTAREA ----
function TextareaInput({ question, value, onChange }) {
  const ref = useRef();
  useEffect(() => { ref.current?.focus(); }, []);
  const appendTranscript = (text) => {
    if (!text) return;
    const existing = (value || '').trim();
    const joined = existing ? existing + ' ' + text : text;
    onChange(joined);
    // refocus so user can keep typing
    setTimeout(() => {
      if (ref.current) {
        ref.current.focus();
        ref.current.setSelectionRange(joined.length, joined.length);
      }
    }, 0);
  };
  return (
    <>
      <div className="voice-textarea-wrap" style={{position: 'relative'}}>
        <textarea
          ref={ref}
          className="textarea"
          value={value || ''}
          placeholder={question.placeholder || ''}
          onChange={(e) => onChange(e.target.value)}
          rows={4}
        />
        <div style={{position: 'absolute', bottom: 10, right: 12}}>
          {window.VoiceDictateButton && (
            <window.VoiceDictateButton onTranscript={appendTranscript} />
          )}
        </div>
      </div>
      {question.id === 'business_description' && (
        <div className="preview-hint">
          <span>💡</span>
          <div><strong>Tip:</strong> mientras más específico seas, mejores serán los ejemplos que vamos generando en las siguientes preguntas.</div>
        </div>
      )}
    </>
  );
}

// ---- CARDS (single choice, pre-written examples) ----
function CardsInput({ question, value, onChange, context }) {
  const [options, setOptions] = useState(question.options || question.fallback || []);
  const [loading, setLoading] = useState(false);
  const [freeform, setFreeform] = useState(false);
  const [freeformText, setFreeformText] = useState('');
  const [seen, setSeen] = useState([]);
  const [dragIdx, setDragIdx] = useState(null);
  const [overIdx, setOverIdx] = useState(null);

  useEffect(() => {
    if (question.aiGenerated && !question.options) {
      regenerate();
    }
  }, [question.id]);

  async function regenerate() {
    setLoading(true);
    const prevLabels = [...seen, ...options.map(o => o.label)].filter(Boolean);
    const opts = await generateExamples(question, context, prevLabels);
    setOptions(opts);
    setSeen(prevLabels);
    setLoading(false);
  }

  const reorderable = !!question.reorderable;
  const moveOption = (from, to) => {
    if (to < 0 || to >= options.length || from === to) return;
    const next = [...options];
    const [item] = next.splice(from, 1);
    next.splice(to, 0, item);
    setOptions(next);
    // keep selected value array in sync w/ new order
    if (question.multi && Array.isArray(value)) {
      const ordered = next.filter(o => value.some(v => v.label === o.label));
      onChange(ordered);
    }
  };

  const multi = !!question.multi;
  const selectedList = multi ? (value || []) : (value ? [value] : []);
  const isSelected = (opt) => selectedList.some(s => s.label === opt.label);
  const handleSelect = (opt) => {
    if (multi) {
      if (isSelected(opt)) {
        onChange(selectedList.filter(s => s.label !== opt.label));
      } else {
        onChange([...selectedList, opt]);
      }
    } else {
      onChange(opt);
    }
  };
  const singleCol = options.length <= 3;

  return (
    <>
      {question.aiGenerated && (
        <div className="ai-badge">
          <span className="pulse"></span>
          {loading ? 'Generando opciones para tu negocio…' : 'Opciones generadas para tu negocio'}
          {multi && !loading && <span style={{marginLeft: 8, color: 'var(--muted)', fontWeight: 500, textTransform: 'none', letterSpacing: 0}}>· puedes elegir varias</span>}
        </div>
      )}
      {!question.aiGenerated && multi && (
        <div style={{fontSize: 12, color: 'var(--muted)', marginBottom: 6}}>Puedes elegir varias</div>
      )}
      {loading ? (
        <div className={`cards-grid ${singleCol ? 'single' : ''}`}>
          {[1,2,3,4].map(i => <div key={i} className="skeleton" style={{height: 96}} />)}
        </div>
      ) : (
        <div className={`cards-grid ${singleCol ? 'single' : ''}`}>
          {options.map((opt, i) => (
            <div
              key={i}
              style={{position: 'relative'}}
              draggable={reorderable}
              onDragStart={reorderable ? (e) => { setDragIdx(i); e.dataTransfer.effectAllowed = 'move'; } : undefined}
              onDragOver={reorderable ? (e) => { e.preventDefault(); setOverIdx(i); } : undefined}
              onDrop={reorderable ? (e) => { e.preventDefault(); if (dragIdx !== null) moveOption(dragIdx, i); setDragIdx(null); setOverIdx(null); } : undefined}
              onDragEnd={reorderable ? () => { setDragIdx(null); setOverIdx(null); } : undefined}
            >
              <button
                className={`card ${isSelected(opt) ? 'selected' : ''}`}
                onClick={() => handleSelect(opt)}
                style={reorderable ? {
                  cursor: dragIdx === i ? 'grabbing' : 'pointer',
                  opacity: dragIdx === i ? 0.4 : 1,
                  borderColor: overIdx === i && dragIdx !== null && dragIdx !== i ? 'var(--violet)' : undefined,
                  boxShadow: overIdx === i && dragIdx !== null && dragIdx !== i ? '0 0 0 3px rgba(119,47,249,0.15)' : undefined,
                  transition: 'border-color 0.15s, box-shadow 0.15s, opacity 0.15s',
                  width: '100%',
                } : undefined}
              >
                {reorderable && (
                  <div style={{position: 'absolute', top: 10, left: 10, color: 'var(--muted)', fontSize: 13, cursor: 'grab', userSelect: 'none'}}>⋮⋮</div>
                )}
                {reorderable && question.multi && isSelected(opt) && (
                  <div style={{position: 'absolute', top: 10, right: 10, width: 20, height: 20, borderRadius: 6, background: 'var(--violet)', color: 'white', display: 'grid', placeItems: 'center', fontFamily: 'var(--font-sans)', fontWeight: 700, fontSize: 11}}>
                    {(value || []).findIndex(v => v.label === opt.label) + 1}
                  </div>
                )}
                {!reorderable && (
                  <div className="card-check">
                    <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
                      <path d="M2 6.5L5 9L10 3.5" stroke="white" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/>
                    </svg>
                  </div>
                )}
                <div className="card-label">
                  <span>{opt.label}</span>
                </div>
                <div className="card-body">{opt.body}</div>
              </button>
            </div>
          ))}
        </div>
      )}
      {question.aiGenerated && (
        <button className={`ai-refresh ${loading ? 'loading' : ''}`} onClick={regenerate}>
          <svg className={loading ? 'spin' : ''} width="12" height="12" viewBox="0 0 12 12" fill="none">
            <path d="M10.5 6a4.5 4.5 0 1 1-1.32-3.18M10.5 1.5v3h-3" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
          {loading ? 'Generando…' : 'Generar otras opciones'}
        </button>
      )}
      {!freeform ? (
        <button className="freeform-toggle" onClick={() => setFreeform(true)}>
          Ninguna aplica — escribir la mía
        </button>
      ) : (
        <div style={{marginTop: 18}}>
          <input
            className="textfield"
            style={{fontSize: 18}}
            autoFocus
            placeholder="Escribe tu opción..."
            value={freeformText}
            onChange={(e) => {
              setFreeformText(e.target.value);
              const custom = { label: e.target.value, body: '', custom: true };
              if (e.target.value.trim()) {
                if (multi) {
                  // Replace any prior custom entry in the list
                  const withoutCustom = selectedList.filter(s => !s.custom);
                  onChange([...withoutCustom, custom]);
                } else {
                  onChange(custom);
                }
              }
            }}
          />
        </div>
      )}
    </>
  );
}

// ---- CHIPS (multi-select) ----
function ChipsInput(props) {
  if (props.question.aiGeneratedChips && window.AIChipsInput) {
    return <window.AIChipsInput {...props} />;
  }
  return <PlainChipsInput {...props} />;
}

function PlainChipsInput({ question, value, onChange }) {
  const selected = value || [];
  const [customChips, setCustomChips] = useState([]);
  const [adding, setAdding] = useState(false);
  const [addText, setAddText] = useState('');

  const allOptions = [...(question.options || []), ...customChips];

  const toggle = (opt) => {
    const next = selected.includes(opt)
      ? selected.filter(x => x !== opt)
      : [...selected, opt];
    onChange(next);
  };

  const addCustom = () => {
    if (!addText.trim()) { setAdding(false); return; }
    const c = addText.trim();
    setCustomChips([...customChips, c]);
    onChange([...selected, c]);
    setAddText('');
    setAdding(false);
  };

  return (
    <div className="chips">
      {allOptions.map((opt) => (
        <button
          key={opt}
          className={`chip ${selected.includes(opt) ? 'selected' : ''}`}
          onClick={() => toggle(opt)}
        >
          {selected.includes(opt) && (
            <svg width="10" height="10" viewBox="0 0 10 10" fill="none">
              <path d="M1.5 5.5L4 8L8.5 2" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
            </svg>
          )}
          {opt}
        </button>
      ))}
      {adding ? (
        <input
          autoFocus
          className="chip"
          style={{border: '1.5px solid var(--violet)', outline: 'none', minWidth: 160}}
          value={addText}
          onChange={(e) => setAddText(e.target.value)}
          onBlur={addCustom}
          onKeyDown={(e) => { if (e.key === 'Enter') addCustom(); }}
          placeholder="Agregar propio…"
        />
      ) : (
        <button className="chip chip-add" onClick={() => setAdding(true)}>
          + agregar
        </button>
      )}
    </div>
  );
}

// ---- A/B COMPARISON ----
function ABInput({ question, value, onChange, context }) {
  const [options, setOptions] = useState(question.fallback || []);
  const [loading, setLoading] = useState(false);
  const [editing, setEditing] = useState(false);
  const [seen, setSeen] = useState([]);

  useEffect(() => {
    if (question.aiGenerated) regenerate();
  }, [question.id]);

  async function regenerate() {
    setLoading(true);
    const prev = [...seen, ...options.map(o => (o.tag || '') + ': ' + (o.sample || ''))];
    const opts = await generateExamples(question, context, prev);
    setOptions(opts.slice(0, 2));
    setSeen(prev);
    setLoading(false);
  }

  const isSelected = (opt) => value?.tag === opt.tag;

  return (
    <>
      {question.aiGenerated && (
        <div className="ai-badge">
          <span className="pulse"></span>
          {loading ? 'Generando dos versiones para tu negocio…' : 'Dos versiones para tu negocio'}
        </div>
      )}
      {loading ? (
        <div className="ab-grid">
          {[1,2].map(i => <div key={i} className="skeleton" style={{height: 240}} />)}
        </div>
      ) : (
        <div className="ab-grid">
          {options.map((opt, i) => (
            <button
              key={i}
              className={`ab-card ${isSelected(opt) ? 'selected' : ''}`}
              onClick={() => { onChange(opt); setEditing(false); }}
            >
              <div className="ab-tag">
                {String.fromCharCode(65 + i)} · {opt.tag}
              </div>
              <div className="ab-sample bubble">{opt.sample}</div>
              {opt.desc && <div className="ab-desc">{opt.desc}</div>}
            </button>
          ))}
        </div>
      )}
      {value && !editing && !loading && (
        <div style={{marginTop: 14, padding: 14, background: '#F6F2FF', border: '1px solid var(--violet)', borderRadius: 12, display: 'flex', gap: 12, alignItems: 'flex-start', justifyContent: 'space-between'}}>
          <div style={{flex: 1}}>
            <div style={{fontSize: 11, textTransform: 'uppercase', letterSpacing: '0.08em', fontWeight: 700, color: 'var(--violet)', marginBottom: 4}}>Elegido — edita si quieres</div>
            <div style={{fontSize: 14.5, color: 'var(--ink)', whiteSpace: 'pre-wrap'}}>{value.sample}</div>
          </div>
          <button onClick={(e) => { e.stopPropagation(); setEditing(true); }} style={{background: 'white', border: '1px solid var(--border)', borderRadius: 999, padding: '6px 14px', fontSize: 12, fontWeight: 600, color: 'var(--violet)', cursor: 'pointer', fontFamily: 'var(--font-sans)'}}>Editar texto</button>
        </div>
      )}
      {value && editing && (
        <div style={{marginTop: 14}}>
          <textarea className="textarea" autoFocus value={value.sample} onChange={(e) => onChange({ ...value, sample: e.target.value })} rows={5} />
          <div style={{display: 'flex', gap: 8, marginTop: 8, justifyContent: 'flex-end'}}>
            <button className="btn btn-ghost" style={{padding: '8px 16px', border: '1px solid var(--border)'}} onClick={() => setEditing(false)}>Listo</button>
          </div>
        </div>
      )}
      {question.aiGenerated && (
        <button className={`ai-refresh ${loading ? 'loading' : ''}`} onClick={regenerate}>
          <svg className={loading ? 'spin' : ''} width="12" height="12" viewBox="0 0 12 12" fill="none">
            <path d="M10.5 6a4.5 4.5 0 1 1-1.32-3.18M10.5 1.5v3h-3" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
          {loading ? 'Generando…' : 'Generar otras dos versiones'}
        </button>
      )}
    </>
  );
}

// ---- RANKING (reorder chosen items) ----
function RankingInput({ question, value, onChange, context }) {
  const source = context[question.dependsOn] || [];
  const current = value || source;

  useEffect(() => {
    if (!value && source.length) onChange(source);
  }, [source.length]);

  const move = (from, to) => {
    if (to < 0 || to >= current.length) return;
    const next = [...current];
    const [item] = next.splice(from, 1);
    next.splice(to, 0, item);
    onChange(next);
  };

  if (!source.length) {
    return <div className="summary-empty">Primero elige algunos objetivos en la pregunta anterior.</div>;
  }

  return (
    <div style={{display: 'flex', flexDirection: 'column', gap: 10, marginTop: 8}}>
      {current.map((item, i) => (
        <div key={item} className="card" style={{display: 'flex', alignItems: 'center', gap: 14, cursor: 'default'}}>
          <div style={{
            width: 28, height: 28, borderRadius: 8,
            background: i === 0 ? 'var(--violet)' : 'var(--paper)',
            color: i === 0 ? 'white' : 'var(--muted)',
            display: 'grid', placeItems: 'center',
            fontFamily: 'var(--font-sans)', fontWeight: 700, fontSize: 13,
          }}>{i + 1}</div>
          <div style={{flex: 1, fontWeight: 600, fontSize: 15}}>{item}</div>
          <button
            onClick={() => move(i, i - 1)}
            disabled={i === 0}
            style={{background: 'none', border: '1px solid var(--border)', borderRadius: 6, padding: '4px 8px', cursor: i === 0 ? 'default' : 'pointer', opacity: i === 0 ? 0.3 : 1}}
            aria-label="subir"
          >↑</button>
          <button
            onClick={() => move(i, i + 1)}
            disabled={i === current.length - 1}
            style={{background: 'none', border: '1px solid var(--border)', borderRadius: 6, padding: '4px 8px', cursor: i === current.length - 1 ? 'default' : 'pointer', opacity: i === current.length - 1 ? 0.3 : 1}}
            aria-label="bajar"
          >↓</button>
        </div>
      ))}
    </div>
  );
}

Object.assign(window, { TextInput, TextareaInput, CardsInput, ChipsInput, ABInput, RankingInput, generateExamples, buildContextSummary });
