// ============================================================================
// McpAddModal — admin-side "create MCP catalog entry" 表单(PR-S.16)
// ============================================================================
//
// 取代 manage-app.jsx 里原 `window.prompt` 简陋的 addMCP() 入口。让 admin
// 一次填全所有字段(server_id / name / description / transport / 连接细节
// / auth_env_key),POST /v1/mcp/servers 创建好后直接选中。
//
// 字段语义(跟 backend CreateMcpServerRequest 一致,见
// src/baizhi_agent_runtime/app/api.py:199):
//   - server_id: required, immutable, primary key。用 kebab-case 习惯
//   - name: display name(可改;sane default = server_id)
//   - description: 给 marketplace listing / Tooltip 用
//   - transport: http | stdio | sse
//   - http_url: transport=http/sse 用
//   - headers_template: request headers template,`${VAR}` runtime 展开
//   - command + args: transport=stdio 用
//   - auth_env_key: env 变量"名"(真 secret 不进 DB),admin 之后在 .env 配
//
// 跟 marketplace 的 McpInstallModal 区别:那条是 user 装 template;
// 这条是 admin 建 template 本身(`mcp_servers` 表)。
//
// config_schema(user install 时的表单 schema)V1 不在本 modal 编辑 ——
// admin 创建后去 editor 里加(future 加 sub-table UI 时再 inline)。

function McpAddModal({ onConfirm, onCancel }) {
  const [values, setValues] = React.useState({
    server_id: "",
    name: "",
    description: "",
    transport: "http",
    http_url: "",
    command: "",
    args: "",
    auth_env_key: "",
    connect_timeout_seconds: "",
    operation_timeout_seconds: "",
    sse_read_timeout_seconds: "",
    sse_session_ttl_seconds: "",
    close_timeout_seconds: "",
    error_content_max_chars: "",
    exception_message_max_chars: "",
  });
  // PR-S.18c · install-time form schema sub-editor。每行 {name, type, required,
  // description, default}。admin 这里加的字段就是 user marketplace install MCP
  // 时 modal 渲染的表单。
  const [configFields, setConfigFields] = React.useState([]);
  const [headers, setHeaders] = React.useState([]);
  const [submitting, setSubmitting] = React.useState(false);
  const [err, setErr] = React.useState(null);

  function addHeader() {
    setHeaders((prev) => [...prev, { k: "", v: "" }]);
  }
  function updateHeader(idx, patch) {
    setHeaders((prev) => prev.map((h, i) => (i === idx ? { ...h, ...patch } : h)));
  }
  function removeHeader(idx) {
    setHeaders((prev) => prev.filter((_, i) => i !== idx));
  }

  function addField() {
    setConfigFields((prev) => [...prev, {
      name: "", type: "secret", required: true, description: "", default: "",
    }]);
  }
  function updateField(idx, patch) {
    setConfigFields((prev) => prev.map((f, i) => (i === idx ? { ...f, ...patch } : f)));
  }
  function removeField(idx) {
    setConfigFields((prev) => prev.filter((_, i) => i !== idx));
  }

  function update(name, val) {
    setValues((prev) => ({ ...prev, [name]: val }));
  }

  function validate() {
    const sid = values.server_id.trim();
    if (!sid) return "server_id is required";
    if (!/^[a-z0-9][a-z0-9_-]*$/i.test(sid)) {
      return "server_id must be alphanumeric, '-' or '_' (no spaces / slashes)";
    }
    if (!values.name.trim()) return "Display name is required";
    if (values.transport === "http" || values.transport === "sse") {
      if (!values.http_url.trim()) return "Endpoint URL is required for http/sse";
    }
    if (values.transport === "stdio") {
      if (!values.command.trim()) return "Command is required for stdio";
    }
    return null;
  }

  function handleSubmit(e) {
    if (e) e.preventDefault();
    if (submitting) return;
    const v = validate();
    if (v) {
      setErr(v);
      return;
    }
    setSubmitting(true);
    setErr(null);
    // Build payload — only include fields relevant to chosen transport;
    // backend ignores irrelevant ones but cleaner not to send noise.
    const payload = {
      server_id: values.server_id.trim(),
      name: values.name.trim(),
      description: values.description.trim(),
      transport: values.transport,
    };
    if (values.transport === "http" || values.transport === "sse") {
      payload.http_url = values.http_url.trim();
      const headerPayload = dictFromKV(headers);
      if (Object.keys(headerPayload).length) payload.headers_template = headerPayload;
    }
    if (values.transport === "stdio") {
      payload.command = values.command.trim();
      const argList = values.args
        .split(/\s+/).filter(Boolean);
      if (argList.length) payload.args = argList;
    }
    if (values.auth_env_key.trim()) {
      payload.auth_env_key = values.auth_env_key.trim();
    }
    const runtimeOptions = runtimeOptionsFromValues(values);
    if (Object.keys(runtimeOptions).length) payload.runtime_options = runtimeOptions;
    // PR-S.18c · config_schema:剔空 name 的行,trim 字段;空表也允许(=
    // 无需 user 填任何 env / secret)。
    const schemaPayload = configFields
      .map((f) => ({
        name: (f.name || "").trim(),
        type: f.type || "string",
        required: !!f.required,
        description: (f.description || "").trim(),
        ...(f.default && String(f.default).trim()
          ? { default: String(f.default).trim() } : {}),
      }))
      .filter((f) => f.name);
    // 前端去重(同 name 会让 user install modal 渲染重复 input)
    const seen = new Set();
    const dedup = [];
    for (const f of schemaPayload) {
      if (seen.has(f.name)) {
        setErr(`config_schema has duplicate field: ${f.name}`);
        setSubmitting(false);
        return;
      }
      seen.add(f.name);
      dedup.push(f);
    }
    if (dedup.length) payload.config_schema = dedup;
    onConfirm(payload).catch((e) => {
      // onConfirm 是 async;失败再开 modal,让 admin 修
      setErr(typeof e === "string" ? e : (e?.message || "Create failed"));
      setSubmitting(false);
    });
  }

  const isHttp = values.transport === "http" || values.transport === "sse";
  const isStdio = values.transport === "stdio";

  return (
    <div
      onClick={onCancel}
      style={{
        position: "fixed", inset: 0, zIndex: 9000,
        background: "rgba(13, 13, 13, 0.42)",
        display: "flex", alignItems: "center", justifyContent: "center",
      }}
    >
      <form
        onClick={(e) => e.stopPropagation()}
        onSubmit={handleSubmit}
        style={{
          background: "var(--bg)", borderRadius: 12,
          border: "1px solid var(--border)",
          boxShadow: "0 24px 64px rgba(0,0,0,0.18)",
          width: 560, maxWidth: "92vw", padding: 24,
          maxHeight: "92vh", overflowY: "auto",
        }}
      >
        <div style={{ display: "flex", alignItems: "baseline", gap: 8 }}>
          <Icon name="plus" size={18} />
          <h2 style={{ margin: 0, fontSize: 16, fontWeight: 600 }}>
            Add MCP server
          </h2>
        </div>
        <p style={{
          margin: "8px 0 16px", fontSize: 12.5, color: "var(--text-muted)",
          lineHeight: 1.55,
        }}>
          Creates a platform catalog entry. Real secret values stay in
          <code style={{
            fontFamily: "var(--font-mono)", fontSize: 11.5,
            padding: "1px 5px", margin: "0 4px",
            background: "var(--bg-subtle)", borderRadius: 3,
          }}>.env</code>
          — admins reference them here by env var <em>name</em> only.
        </p>

        <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          {/* server_id + name 一行 */}
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
            <Field
              label="server_id *"
              hint="immutable, kebab-case"
              mono
              value={values.server_id}
              onChange={(v) => update("server_id", v)}
              placeholder="github-mcp"
            />
            <Field
              label="Display name *"
              value={values.name}
              onChange={(v) => update("name", v)}
              placeholder="GitHub"
            />
          </div>

          <Field
            label="Description"
            multiline
            value={values.description}
            onChange={(v) => update("description", v)}
            placeholder="What this MCP server provides"
          />

          {/* Transport selector */}
          <div>
            <label style={labelStyle}>Transport *</label>
            <select
              value={values.transport}
              onChange={(e) => update("transport", e.target.value)}
              style={inputStyle}
            >
              <option value="http">http / streamableHttp</option>
              <option value="stdio">stdio</option>
              <option value="sse">sse</option>
            </select>
          </div>

          {/* Transport-specific */}
          {isHttp && (
            <>
              <Field
                label="Endpoint URL *"
                mono
                value={values.http_url}
                onChange={(v) => update("http_url", v)}
                placeholder="https://example.com/mcp"
              />

              <div>
                <div style={{ display: "flex", alignItems: "center", marginBottom: 8 }}>
                  <label style={labelStyle}>
                    Headers
                    <span style={{
                      marginLeft: 6, fontWeight: 400, fontSize: 11,
                      color: "var(--text-muted)",
                      fontFamily: "var(--font-sans)",
                    }}>
                      ({headers.length} set · ${"{VAR}"} resolves from runtime env)
                    </span>
                  </label>
                  <div style={{ flex: 1 }} />
                  <button
                    type="button"
                    className="btn ghost sm"
                    onClick={addHeader}
                  >
                    + Add header
                  </button>
                </div>

                {headers.length === 0 ? (
                  <div style={{
                    padding: "10px 12px", fontSize: 12,
                    color: "var(--text-faint)",
                    background: "var(--bg-subtle)",
                    borderRadius: 6, textAlign: "center",
                  }}>
                    No headers configured.
                  </div>
                ) : (
                  <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                    {headers.map((h, i) => (
                      <div
                        key={i}
                        style={{ display: "grid", gridTemplateColumns: "1fr 1.6fr 32px", gap: 8 }}
                      >
                        <input
                          type="text"
                          value={h.k}
                          onChange={(e) => updateHeader(i, { k: e.target.value })}
                          placeholder="header"
                          style={{ ...inputStyle, fontFamily: "var(--font-mono)" }}
                        />
                        <input
                          type="text"
                          value={h.v}
                          onChange={(e) => updateHeader(i, { v: e.target.value })}
                          placeholder="value (use ${VAR} for secrets)"
                          style={{ ...inputStyle, fontFamily: "var(--font-mono)" }}
                        />
                        <button
                          type="button"
                          className="btn ghost icon"
                          onClick={() => removeHeader(i)}
                          title="Remove this header"
                          style={{ height: 30, width: 30 }}
                        >
                          ×
                        </button>
                      </div>
                    ))}
                  </div>
                )}
              </div>
            </>
          )}
          {isStdio && (
            <>
              <Field
                label="Command *"
                mono
                value={values.command}
                onChange={(v) => update("command", v)}
                placeholder="npx, uvx, python …"
              />
              <Field
                label="Arguments"
                hint="space-separated"
                mono
                value={values.args}
                onChange={(v) => update("args", v)}
                placeholder="-y @modelcontextprotocol/server-…"
              />
            </>
          )}

          <div>
            <label style={labelStyle}>
              Runtime options
              <span style={{
                marginLeft: 6, fontWeight: 400, fontSize: 11,
                color: "var(--text-muted)",
                fontFamily: "var(--font-sans)",
              }}>
                blank = runtime default
              </span>
            </label>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
              <Field
                label="Connect timeout (s)"
                mono
                value={values.connect_timeout_seconds}
                onChange={(v) => update("connect_timeout_seconds", v)}
                placeholder="30"
              />
              <Field
                label="Tool/list timeout (s)"
                mono
                value={values.operation_timeout_seconds}
                onChange={(v) => update("operation_timeout_seconds", v)}
                placeholder="120"
              />
              <Field
                label="SSE read timeout (s)"
                mono
                value={values.sse_read_timeout_seconds}
                onChange={(v) => update("sse_read_timeout_seconds", v)}
                placeholder="300"
              />
              <Field
                label="SSE session TTL (s)"
                mono
                value={values.sse_session_ttl_seconds}
                onChange={(v) => update("sse_session_ttl_seconds", v)}
                placeholder="300"
              />
              <Field
                label="Close timeout (s)"
                mono
                value={values.close_timeout_seconds}
                onChange={(v) => update("close_timeout_seconds", v)}
                placeholder="10"
              />
              <Field
                label="Error chars"
                mono
                value={values.error_content_max_chars}
                onChange={(v) => update("error_content_max_chars", v)}
                placeholder="4000"
              />
              <Field
                label="Exception chars"
                mono
                value={values.exception_message_max_chars}
                onChange={(v) => update("exception_message_max_chars", v)}
                placeholder="1500"
              />
            </div>
          </div>

          <Field
            label="Auth env var"
            hint="name only — value stays in .env"
            mono
            value={values.auth_env_key}
            onChange={(v) => update("auth_env_key", v)}
            placeholder="GITHUB_TOKEN"
          />

          {/* PR-S.18c · install-time form schema sub-editor */}
          <div style={{
            borderTop: "1px solid var(--border)",
            paddingTop: 14, marginTop: 6,
          }}>
            <div style={{ display: "flex", alignItems: "center", marginBottom: 8 }}>
              <label style={labelStyle}>
                Install form fields
                <span style={{
                  marginLeft: 6, fontWeight: 400, fontSize: 11,
                  color: "var(--text-muted)",
                  fontFamily: "var(--font-sans)",
                }}>
                  (what users fill at install time — env / secret per user)
                </span>
              </label>
              <div style={{ flex: 1 }} />
              <button
                type="button"
                className="btn ghost sm"
                onClick={addField}
              >
                + Add field
              </button>
            </div>

            {configFields.length === 0 ? (
              <div style={{
                padding: "10px 12px", fontSize: 12,
                color: "var(--text-faint)",
                background: "var(--bg-subtle)",
                borderRadius: 6, textAlign: "center",
              }}>
                No fields — users won't be asked for any env/secret at install.
                Click <strong>+ Add field</strong> to require e.g. a token.
              </div>
            ) : (
              <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                {configFields.map((f, i) => (
                  <div key={i} style={{
                    border: "1px solid var(--border)", borderRadius: 6,
                    padding: 10, background: "var(--bg-subtle)",
                  }}>
                    <div style={{ display: "grid",
                      gridTemplateColumns: "1.4fr 0.9fr 0.7fr 32px", gap: 8 }}>
                      <input
                        type="text"
                        value={f.name}
                        onChange={(e) => updateField(i, { name: e.target.value })}
                        placeholder="SLACK_BOT_TOKEN"
                        style={{ ...inputStyle, fontFamily: "var(--font-mono)" }}
                      />
                      <select
                        value={f.type}
                        onChange={(e) => updateField(i, { type: e.target.value })}
                        style={inputStyle}
                      >
                        <option value="secret">secret (masked)</option>
                        <option value="string">string</option>
                      </select>
                      <label style={{
                        display: "flex", alignItems: "center", gap: 4,
                        fontSize: 12, color: "var(--text-muted)",
                        userSelect: "none", paddingLeft: 4,
                      }}>
                        <input
                          type="checkbox"
                          checked={!!f.required}
                          onChange={(e) => updateField(i, { required: e.target.checked })}
                        />
                        required
                      </label>
                      <button
                        type="button"
                        className="btn ghost icon"
                        onClick={() => removeField(i)}
                        title="Remove this field"
                        style={{ height: 30, width: 30 }}
                      >
                        ×
                      </button>
                    </div>
                    <input
                      type="text"
                      value={f.description || ""}
                      onChange={(e) => updateField(i, { description: e.target.value })}
                      placeholder="Description (shown below the input in the install form)"
                      style={{
                        ...inputStyle, marginTop: 6, fontSize: 12,
                      }}
                    />
                    {f.type === "string" && (
                      <input
                        type="text"
                        value={f.default || ""}
                        onChange={(e) => updateField(i, { default: e.target.value })}
                        placeholder="Default value (string fields only)"
                        style={{
                          ...inputStyle, marginTop: 6, fontSize: 12,
                          fontFamily: "var(--font-mono)",
                        }}
                      />
                    )}
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>

        {err && (
          <div style={{
            marginTop: 14, padding: "8px 12px", borderRadius: 6,
            background: "#fef2f2", color: "#b91c1c",
            fontSize: 12.5, lineHeight: 1.5,
            border: "1px solid #fecaca",
          }}>
            {err}
          </div>
        )}

        <div style={{
          marginTop: 20, display: "flex", justifyContent: "flex-end", gap: 8,
        }}>
          <button type="button" className="btn ghost sm" onClick={onCancel}>
            Cancel
          </button>
          <button
            type="submit"
            className="btn sm"
            disabled={submitting}
            style={{
              background: "var(--text)", color: "#fff", borderColor: "var(--text)",
            }}
          >
            {submitting ? "Creating…" : "Create"}
          </button>
        </div>

        <p style={{
          marginTop: 14, marginBottom: 0, fontSize: 11.5,
          color: "var(--text-faint)", lineHeight: 1.5,
        }}>
          Request headers use <code style={{ fontFamily: "var(--font-mono)" }}>${"{VAR}"}</code>
          interpolation at runtime. Per-user install fields are configured above.
        </p>
      </form>
    </div>
  );
}

// 单字段 helper — keeps the form tidy 同时保持跟 marketplace McpInstallModal
// 类似的视觉。
function Field({ label, hint, value, onChange, placeholder, mono, multiline }) {
  const Component = multiline ? "textarea" : "input";
  return (
    <div>
      <label style={labelStyle}>
        {label}
        {hint && (
          <span style={{
            marginLeft: 6, fontWeight: 400, fontSize: 11,
            color: "var(--text-muted)",
            fontFamily: "var(--font-sans)",
          }}>
            ({hint})
          </span>
        )}
      </label>
      <Component
        type={Component === "input" ? "text" : undefined}
        value={value}
        onChange={(e) => onChange(e.target.value)}
        placeholder={placeholder}
        style={{
          ...inputStyle,
          ...(mono ? { fontFamily: "var(--font-mono)" } : {}),
          ...(multiline ? { minHeight: 56, resize: "vertical" } : {}),
        }}
      />
    </div>
  );
}

function dictFromKV(kvs) {
  const out = {};
  for (const { k, v } of kvs || []) {
    const key = (k || "").trim();
    if (key) out[key] = v;
  }
  return out;
}

function runtimeOptionsFromValues(values) {
  const keys = [
    "connect_timeout_seconds",
    "operation_timeout_seconds",
    "sse_read_timeout_seconds",
    "sse_session_ttl_seconds",
    "close_timeout_seconds",
    "error_content_max_chars",
    "exception_message_max_chars",
  ];
  const out = {};
  for (const key of keys) {
    const raw = String(values[key] || "").trim();
    if (!raw) continue;
    const n = Number(raw);
    if (Number.isFinite(n) && n > 0) out[key] = n;
  }
  return out;
}

const labelStyle = {
  display: "block", fontSize: 12, fontWeight: 500,
  color: "var(--text)", marginBottom: 4,
};
const inputStyle = {
  width: "100%", padding: "7px 10px", fontSize: 12.5,
  border: "1px solid var(--border)", borderRadius: 6,
  background: "white", outline: "none",
  boxSizing: "border-box",
};

window.McpAddModal = McpAddModal;
