// ============================================================================
// public/screens/agent-editor.jsx — Full Agent Editor (Phase 4)
// ----------------------------------------------------------------------------
// Opened when clicking an agent card (agents.jsx sends go("agent_edit:ID")).
// Sections:
//   Left column  — Identity, Prompt, Opening Script, Objection Map
//   Right column — Voice Picker, LLM Config, Temperature, Version History
//   Bottom       — Test Call (Hidrogen Live preview)
//
// The editor auto-saves on blur (debounced PATCH). Publish creates a version.
// ============================================================================

function AgentEditorScreen({ agentId, go }) {
  const [agent, setAgent]     = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [error, setError]     = React.useState(null);
  const [saving, setSaving]   = React.useState(false);
  const [dirty, setDirty]     = React.useState(false);
  const [versions, setVers]   = React.useState([]);
  const [voices, setVoices]   = React.useState([]);
  const [showVoice, setShowVoice] = React.useState(false);
  const [showVersions, setShowVersions] = React.useState(false);
  const [publishNote, setPublishNote] = React.useState("");
  const [publishing, setPublishing]   = React.useState(false);
  const [tab, setTab] = React.useState("prompt"); // prompt | voice | llm | objections | test

  // Load agent
  const loadAgent = React.useCallback(async () => {
    setLoading(true);
    setError(null);
    const r = await VoaisAPI.get("/api/agents/" + agentId);
    if (r.ok && r.data?.ok) {
      const a = r.data.agent;
      // Parse objection_json if string
      if (typeof a.objection_json === "string") {
        try { a.objection_json = JSON.parse(a.objection_json); } catch (_) { a.objection_json = null; }
      }
      setAgent(a);
    } else {
      setError(r.data?.msg || "Failed to load agent.");
    }
    setLoading(false);
  }, [agentId]);

  // Load voices catalog
  const loadVoices = React.useCallback(async () => {
    const r = await VoaisAPI.get("/api/voices");
    if (r.ok && r.data?.ok) setVoices(r.data.voices);
  }, []);

  // Load versions
  const loadVersions = React.useCallback(async () => {
    const r = await VoaisAPI.get("/api/agents/" + agentId + "/versions");
    if (r.ok && r.data?.ok) setVers(r.data.versions);
  }, [agentId]);

  React.useEffect(() => {
    loadAgent();
    loadVoices();
    loadVersions();
  }, [loadAgent, loadVoices, loadVersions]);

  // Update agent field locally
  const updateField = (field, value) => {
    setAgent(prev => ({ ...prev, [field]: value }));
    setDirty(true);
  };

  // Save (PATCH) — debounced auto-save or manual
  const saveRef = React.useRef(null);
  const save = React.useCallback(async (fields) => {
    if (!fields || !Object.keys(fields).length) return;
    setSaving(true);
    const r = await VoaisAPI.patch("/api/agents/" + agentId, fields);
    setSaving(false);
    if (r.ok) setDirty(false);
    return r;
  }, [agentId]);

  // Auto-save on blur with debounce
  const debouncedSave = React.useCallback((fields) => {
    if (saveRef.current) clearTimeout(saveRef.current);
    saveRef.current = setTimeout(() => save(fields), 800);
  }, [save]);

  // Publish
  const handlePublish = async () => {
    // Save any pending changes first
    if (dirty) {
      await save(buildPatchPayload());
    }
    setPublishing(true);
    const r = await VoaisAPI.post("/api/agents/" + agentId + "/publish", { note: publishNote || null });
    setPublishing(false);
    if (r.ok) {
      setPublishNote("");
      loadAgent();
      loadVersions();
    } else {
      alert(r.data?.msg || "Publish failed.");
    }
  };

  // Rollback
  const handleRollback = async (vid) => {
    if (!confirm("Restore this version? Current unsaved changes will be lost.")) return;
    const r = await VoaisAPI.post("/api/agents/" + agentId + "/versions/" + vid + "/rollback");
    if (r.ok) {
      loadAgent();
      loadVersions();
    } else {
      alert(r.data?.msg || "Rollback failed.");
    }
  };

  // Build patch payload from current agent state
  const buildPatchPayload = () => {
    if (!agent) return {};
    return {
      name: agent.name, role: agent.role, description: agent.description,
      language: agent.language, system_prompt: agent.system_prompt,
      opening_script: agent.opening_script, voice_tone: agent.voice_tone,
      voice_id: agent.voice_id, llm_provider: agent.llm_provider,
      llm_model: agent.llm_model, temperature: agent.temperature,
      objection_json: agent.objection_json,
    };
  };

  if (loading) return <DashboardSkeleton/>;
  if (error) return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      <div className="auth-banner err"><I.alert size={14}/><span>{error}</span></div>
      <Btn kind="ghost" icon={<I.arrow_left size={14}/>} onClick={() => go("agents")}>Back to agents</Btn>
    </div>
  );
  if (!agent) return null;

  const selectedVoice = voices.find(v => v.id === agent.voice_id);
  const statusTone = agent.status === "active" ? "green" : agent.status === "draft" ? "gray" : "yellow";

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: "var(--gap-grid)" }}>

      {/* ── Top bar ──────────────────────────────────────────────────── */}
      <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
        <Btn kind="ghost" size="sm" icon={<I.arrow_left size={13}/>} onClick={() => go("agents")}>Agents</Btn>
        <div style={{ width: 1, height: 20, background: "var(--line)" }}/>
        <span style={{ fontSize: 16, fontWeight: 600 }}>{agent.name}</span>
        <Badge tone="gray">{agent.code}</Badge>
        <Badge tone={statusTone} dot>{agent.status}</Badge>
        <div style={{ flex: 1 }}/>
        {saving && <span style={{ fontSize: 12, color: "var(--ink-3)" }}>Saving...</span>}
        {dirty && !saving && <span style={{ fontSize: 12, color: "var(--warn)" }}>Unsaved changes</span>}
        <Btn kind="ghost" size="sm" onClick={() => setShowVersions(true)} icon={<I.history size={13}/>}>
          v{agent.published_version || 0}
        </Btn>
        <Btn kind="primary" size="sm" icon={<I.zap size={13}/>} onClick={handlePublish} disabled={publishing}>
          {publishing ? "Publishing..." : "Publish"}
        </Btn>
      </div>

      {/* ── Tab nav ──────────────────────────────────────────────────── */}
      <Tabs value={tab} onChange={setTab} options={[
        { value: "prompt",     label: "Prompt" },
        { value: "voice",      label: "Voice" },
        { value: "llm",        label: "LLM Config" },
        { value: "objections", label: "Objections" },
        { value: "test",       label: "Test Call" },
      ]}/>

      {/* ── Prompt tab ───────────────────────────────────────────────── */}
      {tab === "prompt" && (
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "var(--gap-grid)" }}>
          <Card>
            <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
              <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 4 }}>Identity</div>
              <Field label="Agent name">
                <input className="input" value={agent.name || ""} onChange={e => updateField("name", e.target.value)}
                  onBlur={() => debouncedSave({ name: agent.name })}/>
              </Field>
              <Field label="Role" hint="Shown on agent card">
                <input className="input" value={agent.role || ""} onChange={e => updateField("role", e.target.value)}
                  onBlur={() => debouncedSave({ role: agent.role })}/>
              </Field>
              <Field label="Description">
                <textarea className="textarea" rows={2} value={agent.description || ""}
                  onChange={e => updateField("description", e.target.value)}
                  onBlur={() => debouncedSave({ description: agent.description })}/>
              </Field>
              <Field label="Language">
                <select className="select" value={agent.language || "hinglish"}
                  onChange={e => { updateField("language", e.target.value); save({ language: e.target.value }); }}>
                  <option value="hinglish">Hinglish</option>
                  <option value="hindi">Hindi</option>
                  <option value="english">English</option>
                  <option value="tamil">Tamil</option>
                  <option value="telugu">Telugu</option>
                  <option value="bengali">Bengali</option>
                </select>
              </Field>
            </div>
          </Card>
          <Card>
            <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
              <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 4 }}>Prompt configuration</div>
              <Field label="System prompt" hint={`${(agent.system_prompt || "").length}/4000 chars — the base instructions for every call`}>
                <textarea className="textarea" rows={8} value={agent.system_prompt || ""}
                  placeholder="You are Priya, a warm and friendly sales agent for Mercedes-Benz Lucknow. You speak Hinglish naturally..."
                  onChange={e => updateField("system_prompt", e.target.value)}
                  onBlur={() => debouncedSave({ system_prompt: agent.system_prompt })}
                  style={{ fontFamily: "var(--font-mono, monospace)", fontSize: 13 }}/>
              </Field>
              <Field label="Opening script" hint="The greeting line when the call connects">
                <textarea className="textarea" rows={3} value={agent.opening_script || ""}
                  placeholder="Namaste! Main Priya bol rahi hoon Mercedes-Benz Lucknow se..."
                  onChange={e => updateField("opening_script", e.target.value)}
                  onBlur={() => debouncedSave({ opening_script: agent.opening_script })}/>
              </Field>
              <Field label="Voice tone" hint="Style hint for TTS (e.g. calm, warm, professional)">
                <input className="input" value={agent.voice_tone || ""}
                  placeholder="calm, warm and professional"
                  onChange={e => updateField("voice_tone", e.target.value)}
                  onBlur={() => debouncedSave({ voice_tone: agent.voice_tone })}/>
              </Field>
            </div>
          </Card>
        </div>
      )}

      {/* ── Voice tab ────────────────────────────────────────────────── */}
      {tab === "voice" && (
        <Card>
          <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
              <div style={{ fontSize: 14, fontWeight: 600 }}>Voice</div>
              <div style={{ flex: 1 }}/>
              <Btn kind="ghost" size="sm" icon={<I.mic size={13}/>} onClick={() => setShowVoice(true)}>
                {selectedVoice ? "Change voice" : "Pick a voice"}
              </Btn>
            </div>

            {selectedVoice ? (
              <VoiceCard voice={selectedVoice} selected onRemove={() => { updateField("voice_id", null); save({ voice_id: null }); }}/>
            ) : (
              <div style={{ padding: 32, textAlign: "center", color: "var(--ink-3)", border: "1.5px dashed var(--line)", borderRadius: 12, cursor: "pointer" }}
                   onClick={() => setShowVoice(true)}>
                <I.mic size={28} style={{ opacity: 0.4, marginBottom: 8 }}/>
                <div style={{ fontSize: 13 }}>No voice selected — click to browse the voice catalog</div>
              </div>
            )}

            {/* Quick voice grid */}
            <div style={{ fontSize: 13, fontWeight: 600, marginTop: 8 }}>Available voices</div>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))", gap: 10 }}>
              {voices.slice(0, 8).map(v => (
                <VoiceCard key={v.id} voice={v} compact
                  selected={agent.voice_id === v.id}
                  onSelect={() => { updateField("voice_id", v.id); save({ voice_id: v.id }); }}/>
              ))}
            </div>
            {voices.length > 8 && (
              <Btn kind="ghost" size="sm" onClick={() => setShowVoice(true)}>View all {voices.length} voices</Btn>
            )}
          </div>
        </Card>
      )}

      {/* ── LLM Config tab ───────────────────────────────────────────── */}
      {tab === "llm" && (
        <Card>
          <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
            <div style={{ fontSize: 14, fontWeight: 600 }}>LLM configuration</div>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
              <Field label="Provider">
                <select className="select" value={agent.llm_provider || "gemini"}
                  onChange={e => { updateField("llm_provider", e.target.value); save({ llm_provider: e.target.value }); }}>
                  <option value="gemini">Google Gemini</option>
                  <option value="openai">OpenAI</option>
                  <option value="anthropic">Anthropic Claude</option>
                  <option value="custom">Custom / Self-hosted</option>
                </select>
              </Field>
              <Field label="Model">
                <input className="input" value={agent.llm_model || ""}
                  placeholder="gemini-1.5-pro"
                  onChange={e => updateField("llm_model", e.target.value)}
                  onBlur={() => debouncedSave({ llm_model: agent.llm_model })}/>
              </Field>
            </div>
            <Field label={`Temperature: ${agent.temperature ?? 0.7}`} hint="0 = precise, 1 = creative">
              <input type="range" min="0" max="1" step="0.05"
                value={agent.temperature ?? 0.7}
                style={{ width: "100%" }}
                onChange={e => updateField("temperature", parseFloat(e.target.value))}
                onMouseUp={() => save({ temperature: agent.temperature })}
                onTouchEnd={() => save({ temperature: agent.temperature })}/>
              <div style={{ display: "flex", justifyContent: "space-between", fontSize: 11, color: "var(--ink-3)", marginTop: 4 }}>
                <span>Precise (0)</span>
                <span>Balanced (0.5)</span>
                <span>Creative (1)</span>
              </div>
            </Field>
          </div>
        </Card>
      )}

      {/* ── Objections tab ───────────────────────────────────────────── */}
      {tab === "objections" && (
        <Card>
          <ObjectionEditor
            objections={agent.objection_json || {}}
            onChange={(obj) => { updateField("objection_json", obj); debouncedSave({ objection_json: obj }); }}
          />
        </Card>
      )}

      {/* ── Test Call tab ────────────────────────────────────────────── */}
      {tab === "test" && (
        <Card>
          <TestCallPanel agent={agent}/>
        </Card>
      )}

      {/* ── Voice Picker Modal ───────────────────────────────────────── */}
      {showVoice && (
        <VoicePickerModal
          voices={voices}
          selectedId={agent.voice_id}
          onSelect={(id) => {
            updateField("voice_id", id);
            save({ voice_id: id });
            setShowVoice(false);
          }}
          onClose={() => setShowVoice(false)}
        />
      )}

      {/* ── Version History Modal ────────────────────────────────────── */}
      {showVersions && (
        <Modal title="Version history" onClose={() => setShowVersions(false)} width={520}>
          <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            {/* Publish new */}
            <div style={{ display: "flex", gap: 8, marginBottom: 8 }}>
              <input className="input" style={{ flex: 1 }} placeholder="Version note (optional)"
                value={publishNote} onChange={e => setPublishNote(e.target.value)}/>
              <Btn kind="primary" size="sm" onClick={handlePublish} disabled={publishing}>
                {publishing ? "..." : "Publish"}
              </Btn>
            </div>
            {versions.length === 0 && (
              <div style={{ padding: 24, textAlign: "center", color: "var(--ink-3)", fontSize: 13 }}>
                No versions yet. Click Publish to create the first snapshot.
              </div>
            )}
            {versions.map(v => (
              <div key={v.id} style={{
                display: "flex", alignItems: "center", gap: 10, padding: "10px 12px",
                background: "var(--surface-2)", borderRadius: 10,
              }}>
                <Badge tone={v.version === agent.published_version ? "green" : "gray"}>v{v.version}</Badge>
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 13, fontWeight: 500 }}>{v.note || "No note"}</div>
                  <div style={{ fontSize: 11, color: "var(--ink-3)" }}>
                    {new Date(v.published_at).toLocaleString("en-IN", { day: "numeric", month: "short", year: "numeric", hour: "2-digit", minute: "2-digit" })}
                  </div>
                </div>
                {v.version !== agent.published_version && (
                  <Btn kind="ghost" size="sm" onClick={() => handleRollback(v.id)}>Restore</Btn>
                )}
                {v.version === agent.published_version && (
                  <Badge tone="green" dot>Current</Badge>
                )}
              </div>
            ))}
          </div>
        </Modal>
      )}
    </div>
  );
}


// ── Voice Card (compact or full) ────────────────────────────────────────
function VoiceCard({ voice, compact, selected, onSelect, onRemove }) {
  const providerColors = {
    sarvam: "#00D49F", elevenlabs: "#A78BFA", azure: "#5B8FFF",
    openai: "#4DD0E1", gemini: "#FFD27D", hidrogen: "#FF8A3D", murf: "#FF6F91",
  };
  const color = providerColors[voice.provider] || "var(--ink-3)";
  const tags = voice.tags || [];

  if (compact) {
    return (
      <div onClick={onSelect} style={{
        padding: "10px 12px", borderRadius: 10, cursor: "pointer",
        background: selected ? "var(--accent-soft)" : "var(--surface-2)",
        border: selected ? "1.5px solid var(--accent)" : "1.5px solid transparent",
        transition: "all 0.15s",
      }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <div style={{ width: 8, height: 8, borderRadius: "50%", background: color, flexShrink: 0 }}/>
          <span style={{ fontSize: 13, fontWeight: 500 }}>{voice.display_name}</span>
        </div>
        <div style={{ fontSize: 11, color: "var(--ink-3)", marginTop: 4 }}>
          {voice.provider} · {voice.gender} · {voice.language}
        </div>
      </div>
    );
  }

  return (
    <div style={{
      padding: "14px 16px", borderRadius: 12, background: "var(--surface-2)",
      border: selected ? "1.5px solid var(--accent)" : "1.5px solid var(--line)",
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
        <div style={{ width: 40, height: 40, borderRadius: 10, background: color + "22",
          display: "grid", placeItems: "center", color, fontWeight: 700, fontSize: 16 }}>
          {voice.display_name.charAt(0)}
        </div>
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 14, fontWeight: 600 }}>{voice.display_name}</div>
          <div style={{ fontSize: 12, color: "var(--ink-3)" }}>{voice.provider} · {voice.gender} · {voice.language}</div>
        </div>
        {onRemove && <Btn kind="ghost" size="sm" onClick={onRemove} title="Remove"><I.x size={13}/></Btn>}
      </div>
      {tags.length > 0 && (
        <div style={{ display: "flex", gap: 6, marginTop: 10, flexWrap: "wrap" }}>
          {tags.map(t => <Badge key={t} tone="blue">{t}</Badge>)}
        </div>
      )}
      {voice.tier !== "standard" && (
        <Badge tone="yellow" style={{ marginTop: 8 }}>{voice.tier}</Badge>
      )}
    </div>
  );
}


// ── Voice Picker Modal ──────────────────────────────────────────────────
function VoicePickerModal({ voices, selectedId, onSelect, onClose }) {
  const [search, setSearch]     = React.useState("");
  const [filter, setFilter]     = React.useState("all"); // all | sarvam | elevenlabs | ...
  const [genderF, setGenderF]   = React.useState("all");
  const [previewId, setPreviewId] = React.useState(null);
  const [previewLoading, setPreviewLoading] = React.useState(false);
  const audioRef = React.useRef(null);

  const providers = [...new Set(voices.map(v => v.provider))];

  const filtered = voices.filter(v => {
    if (filter !== "all" && v.provider !== filter) return false;
    if (genderF !== "all" && v.gender !== genderF) return false;
    if (search) {
      const q = search.toLowerCase();
      const nameMatch = v.display_name.toLowerCase().includes(q);
      const tagMatch = (v.tags || []).some(t => t.toLowerCase().includes(q));
      if (!nameMatch && !tagMatch) return false;
    }
    return true;
  });

  // Preview voice via Hidrogen
  const handlePreview = async (voiceId) => {
    if (previewLoading) return;
    setPreviewId(voiceId);
    setPreviewLoading(true);
    try {
      const r = await VoaisAPI.post("/api/voices/" + voiceId + "/preview", {});
      if (r.ok && r.data?.ok && r.data.audio?.data) {
        playPCM16(r.data.audio.data, r.data.audio.sample_rate || 24000);
      } else {
        console.warn("Preview failed:", r.data?.msg);
      }
    } catch (e) {
      console.warn("Preview error:", e);
    }
    setPreviewLoading(false);
  };

  return (
    <Modal title="Voice catalog" onClose={onClose} width={680}>
      <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
        {/* Filters */}
        <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center" }}>
          <div style={{ position: "relative", flex: 1, minWidth: 180 }}>
            <I.search size={14} style={{ position: "absolute", left: 10, top: "50%", transform: "translateY(-50%)", color: "var(--ink-3)" }}/>
            <input className="input" placeholder="Search voices..." value={search}
              onChange={e => setSearch(e.target.value)}
              style={{ paddingLeft: 32 }}/>
          </div>
          <select className="select" value={filter} onChange={e => setFilter(e.target.value)} style={{ width: 140 }}>
            <option value="all">All providers</option>
            {providers.map(p => <option key={p} value={p}>{p.charAt(0).toUpperCase() + p.slice(1)}</option>)}
          </select>
          <select className="select" value={genderF} onChange={e => setGenderF(e.target.value)} style={{ width: 120 }}>
            <option value="all">All genders</option>
            <option value="female">Female</option>
            <option value="male">Male</option>
            <option value="neutral">Neutral</option>
          </select>
        </div>

        {/* Grid */}
        <div style={{ maxHeight: 420, overflowY: "auto", display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          {filtered.map(v => (
            <VoicePickerCard key={v.id} voice={v}
              selected={selectedId === v.id}
              previewing={previewId === v.id && previewLoading}
              onSelect={() => onSelect(v.id)}
              onPreview={() => handlePreview(v.id)}
            />
          ))}
          {filtered.length === 0 && (
            <div style={{ gridColumn: "1/3", padding: 32, textAlign: "center", color: "var(--ink-3)", fontSize: 13 }}>
              No voices match your filters.
            </div>
          )}
        </div>
      </div>
    </Modal>
  );
}

function VoicePickerCard({ voice, selected, previewing, onSelect, onPreview }) {
  const providerColors = {
    sarvam: "#00D49F", elevenlabs: "#A78BFA", azure: "#5B8FFF",
    openai: "#4DD0E1", gemini: "#FFD27D", hidrogen: "#FF8A3D",
  };
  const color = providerColors[voice.provider] || "var(--ink-3)";
  const tags = voice.tags || [];

  return (
    <div style={{
      padding: "12px 14px", borderRadius: 10, cursor: "pointer",
      background: selected ? "var(--accent-soft)" : "var(--surface-2)",
      border: selected ? "1.5px solid var(--accent)" : "1.5px solid transparent",
      transition: "all 0.15s",
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
        <div style={{
          width: 36, height: 36, borderRadius: 8,
          background: color + "22", display: "grid", placeItems: "center",
          color, fontWeight: 700, fontSize: 15, flexShrink: 0,
        }}>
          {voice.display_name.charAt(0)}
        </div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 13, fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
            {voice.display_name}
          </div>
          <div style={{ fontSize: 11, color: "var(--ink-3)" }}>
            {voice.provider} · {voice.gender} · {voice.language}
          </div>
        </div>
        <div style={{ display: "flex", gap: 4 }}>
          <button className="topbar-icon-btn" title="Preview" onClick={e => { e.stopPropagation(); onPreview(); }}
            style={{ opacity: previewing ? 0.5 : 1 }}>
            {previewing ? <I.pause size={13}/> : <I.play size={13}/>}
          </button>
        </div>
      </div>
      {tags.length > 0 && (
        <div style={{ display: "flex", gap: 4, marginTop: 8, flexWrap: "wrap" }}>
          {tags.map(t => <Badge key={t} tone="gray">{t}</Badge>)}
        </div>
      )}
      <div style={{ marginTop: 8 }}>
        <Btn kind={selected ? "primary" : "ghost"} size="sm" style={{ width: "100%" }}
          onClick={onSelect}>
          {selected ? "Selected" : "Select"}
        </Btn>
      </div>
    </div>
  );
}


// ── Objection Map Editor ────────────────────────────────────────────────
function ObjectionEditor({ objections, onChange }) {
  const entries = Object.entries(objections || {});
  const [newKey, setNewKey]   = React.useState("");
  const [newVal, setNewVal]   = React.useState("");

  const update = (oldKey, newK, newV) => {
    const obj = { ...objections };
    if (oldKey !== newK) delete obj[oldKey];
    obj[newK] = newV;
    onChange(obj);
  };

  const remove = (key) => {
    const obj = { ...objections };
    delete obj[key];
    onChange(obj);
  };

  const add = () => {
    if (!newKey.trim()) return;
    onChange({ ...objections, [newKey.trim()]: newVal.trim() });
    setNewKey("");
    setNewVal("");
  };

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
        <div style={{ fontSize: 14, fontWeight: 600 }}>Objection handling map</div>
        <Badge tone="gray">{entries.length} rules</Badge>
      </div>
      <div style={{ fontSize: 12, color: "var(--ink-3)", lineHeight: 1.5 }}>
        When the caller raises an objection, the AI will use these scripted responses.
        The AI matches caller intent to the closest objection key and delivers the response.
      </div>

      {entries.map(([key, val], i) => (
        <div key={i} style={{ display: "grid", gridTemplateColumns: "1fr 1fr auto", gap: 8, alignItems: "start" }}>
          <div>
            <label className="field-label">Objection</label>
            <input className="input" value={key}
              onChange={e => update(key, e.target.value, val)}/>
          </div>
          <div>
            <label className="field-label">Response</label>
            <textarea className="textarea" rows={2} value={val}
              onChange={e => update(key, key, e.target.value)}/>
          </div>
          <Btn kind="ghost" size="sm" onClick={() => remove(key)} title="Remove"
            style={{ marginTop: 22 }}><I.trash size={13}/></Btn>
        </div>
      ))}

      {/* Add new */}
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr auto", gap: 8, alignItems: "end",
        padding: "12px 14px", background: "var(--surface-2)", borderRadius: 10 }}>
        <Field label="New objection">
          <input className="input" placeholder="Too expensive" value={newKey}
            onChange={e => setNewKey(e.target.value)}/>
        </Field>
        <Field label="Response">
          <textarea className="textarea" rows={2} placeholder="I understand budget is important..."
            value={newVal} onChange={e => setNewVal(e.target.value)}/>
        </Field>
        <Btn kind="primary" size="sm" icon={<I.plus size={13}/>} onClick={add}
          disabled={!newKey.trim()}>Add</Btn>
      </div>
    </div>
  );
}


// ── Test Call Panel (Hidrogen Live WebSocket) ───────────────────────────
function TestCallPanel({ agent }) {
  const [status, setStatus]   = React.useState("idle"); // idle | connecting | ready | active | ended
  const [error, setError]     = React.useState(null);
  const [turns, setTurns]     = React.useState([]);
  const wsRef    = React.useRef(null);
  const audioCtx = React.useRef(null);
  const streamRef = React.useRef(null);
  const processorRef = React.useRef(null);
  const playQueue = React.useRef([]);
  const playingRef = React.useRef(false);

  const startCall = async () => {
    setError(null);
    setTurns([]);
    setStatus("connecting");

    try {
      // Get Hidrogen API key from tenant settings
      // For now we fetch it via a simple check — in production this would be handled server-side
      // We'll use a relay endpoint or pass key to browser securely
      // For test call, we use browser WebSocket directly with the key from /api/config
      const configR = await VoaisAPI.get("/api/voices/1/preview"); // just to test if key exists
      // Actually, let's build a proper test-call endpoint later.
      // For now, show a placeholder with instructions.

      setError("Test call requires the Hidrogen API key to be configured. The full browser-to-Hidrogen WebSocket bridge will be wired in the next iteration.");
      setStatus("idle");
    } catch (e) {
      setError(e.message);
      setStatus("idle");
    }
  };

  const endCall = () => {
    if (wsRef.current) {
      try { wsRef.current.close(1000); } catch (_) {}
    }
    if (streamRef.current) {
      streamRef.current.getTracks().forEach(t => t.stop());
    }
    if (audioCtx.current) {
      audioCtx.current.close();
    }
    setStatus("ended");
  };

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
        <div style={{ fontSize: 14, fontWeight: 600 }}>Test call</div>
        <Badge tone="blue">Hidrogen Live</Badge>
      </div>
      <div style={{ fontSize: 12, color: "var(--ink-3)", lineHeight: 1.5 }}>
        Talk to your agent in real-time using your browser microphone. The agent will use the
        current system prompt, opening script, and voice settings.
      </div>

      {error && <div className="auth-banner err"><I.alert size={14}/><span>{error}</span></div>}

      <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
        {status === "idle" || status === "ended" ? (
          <Btn kind="primary" icon={<I.phone size={14}/>} onClick={startCall}>Start test call</Btn>
        ) : status === "connecting" ? (
          <Btn kind="ghost" disabled>Connecting...</Btn>
        ) : (
          <Btn kind="ghost" icon={<I.x size={14}/>} onClick={endCall} style={{ color: "var(--err)" }}>End call</Btn>
        )}
        {(status === "active" || status === "ready") && <Waveform count={24} height={28} running/>}
      </div>

      {/* Transcript */}
      {turns.length > 0 && (
        <div style={{ maxHeight: 240, overflowY: "auto", display: "flex", flexDirection: "column", gap: 6 }}>
          {turns.map((t, i) => (
            <div key={i} style={{
              padding: "8px 12px", borderRadius: 10, fontSize: 13, lineHeight: 1.5,
              background: t.role === "assistant" ? "var(--accent-soft)" : "var(--surface-2)",
              alignSelf: t.role === "assistant" ? "flex-start" : "flex-end",
              maxWidth: "80%",
            }}>
              <div style={{ fontSize: 11, fontWeight: 600, color: "var(--ink-3)", marginBottom: 2 }}>
                {t.role === "assistant" ? agent.name : "You"}
              </div>
              {t.text}
            </div>
          ))}
        </div>
      )}

      {/* Agent config preview */}
      <div style={{ padding: "12px 14px", background: "var(--surface-2)", borderRadius: 10 }}>
        <div style={{ fontSize: 12, fontWeight: 600, marginBottom: 8, color: "var(--ink-3)" }}>Test call will use:</div>
        <div style={{ display: "grid", gridTemplateColumns: "auto 1fr", gap: "4px 12px", fontSize: 12 }}>
          <span style={{ color: "var(--ink-3)" }}>Prompt:</span>
          <span>{(agent.system_prompt || "—").slice(0, 80)}{(agent.system_prompt || "").length > 80 ? "..." : ""}</span>
          <span style={{ color: "var(--ink-3)" }}>Greeting:</span>
          <span>{agent.opening_script || "—"}</span>
          <span style={{ color: "var(--ink-3)" }}>LLM:</span>
          <span>{agent.llm_provider} / {agent.llm_model}</span>
          <span style={{ color: "var(--ink-3)" }}>Temperature:</span>
          <span>{agent.temperature ?? 0.7}</span>
        </div>
      </div>
    </div>
  );
}


// ── PCM16 audio playback helper ─────────────────────────────────────────
function playPCM16(base64Data, sampleRate) {
  try {
    const audioCtx = new (window.AudioContext || window.webkitAudioContext)({ sampleRate });
    const raw = atob(base64Data);
    const bytes = new Uint8Array(raw.length);
    for (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i);
    const pcm16 = new Int16Array(bytes.buffer);
    const floats = new Float32Array(pcm16.length);
    for (let i = 0; i < pcm16.length; i++) floats[i] = pcm16[i] / 32768;

    const buf = audioCtx.createBuffer(1, floats.length, sampleRate);
    buf.getChannelData(0).set(floats);
    const src = audioCtx.createBufferSource();
    src.buffer = buf;
    src.connect(audioCtx.destination);
    src.start();
    src.onended = () => audioCtx.close();
  } catch (e) {
    console.error("playPCM16 error:", e);
  }
}


// Expose globally
window.AgentEditorScreen = AgentEditorScreen;
