// 静态调试页通过配置 API 获取实际 Swagger JSON 地址，避免硬编码路由前缀。
const DEBUG_CONFIG_URL = "/api/debug/config";
const STORAGE_PREFIX = "flyshot.debug.";
const HISTORY_LIMIT = 10;

const groupTitleByPrefix = [
  // 基础与状态分组：探活和状态快照两个固定 API 路径
  { match: function (op) { return op.path === "/healthz" || op.path === "/api/status/snapshot"; }, title: "基础与状态" },
  // 默认兜底：剩余全部走 ControllerClient 兼容分组
  { match: function () { return true; }, title: "ControllerClient 兼容" }
];

const state = {
  spec: null,
  operations: [],
  history: []
};

const requestBodySamples = {
  "POST /move_pose/": {
    x: 100.0,
    y: 200.0,
    z: 300.0,
    w: 0.0,
    p: 45.0,
    r: 0.0
  }
};

/** 简单的 escape：把任意字符串安全嵌入 textContent 之外的位置时使用。 */
function escapeHtml(value) {
  return String(value).replace(/[&<>"']/g, function (ch) {
    return { "&": "&amp;", "<": "&lt;", ">": "&gt;", "\"": "&quot;", "'": "&#39;" }[ch];
  });
}

/** 解析 OpenAPI 中的 $ref 引用，仅支持本地 components.schemas 形式。 */
function resolveRef(ref) {
  if (!ref || !state.spec) return null;
  const parts = ref.replace(/^#\//, "").split("/");
  let cursor = state.spec;
  for (const part of parts) {
    if (cursor && Object.prototype.hasOwnProperty.call(cursor, part)) {
      cursor = cursor[part];
    } else {
      return null;
    }
  }
  return cursor;
}

/** 根据 schema 生成默认 JSON 模板，用于自动填充请求体编辑器。 */
function buildSampleFromSchema(schema, depth) {
  depth = depth || 0;
  // 防御递归：复杂自引用 schema 在 4 层后停下，避免栈爆。
  if (!schema || depth > 4) return null;

  if (schema.$ref) {
    const resolved = resolveRef(schema.$ref);
    return resolved ? buildSampleFromSchema(resolved, depth + 1) : null;
  }

  // 部分 schema 只标 oneOf/anyOf/allOf，挑第一个分支即可，调试场景够用。
  if (Array.isArray(schema.oneOf) && schema.oneOf.length > 0) return buildSampleFromSchema(schema.oneOf[0], depth + 1);
  if (Array.isArray(schema.anyOf) && schema.anyOf.length > 0) return buildSampleFromSchema(schema.anyOf[0], depth + 1);
  if (Array.isArray(schema.allOf) && schema.allOf.length > 0) {
    const merged = {};
    schema.allOf.forEach(function (sub) {
      const value = buildSampleFromSchema(sub, depth + 1);
      if (value && typeof value === "object" && !Array.isArray(value)) Object.assign(merged, value);
    });
    return merged;
  }

  const type = schema.type || (schema.properties ? "object" : "string");
  switch (type) {
    case "object": {
      const result = {};
      const props = schema.properties || {};
      Object.keys(props).forEach(function (key) {
        result[key] = buildSampleFromSchema(props[key], depth + 1);
      });
      return result;
    }
    case "array":
      return [];
    case "integer":
    case "number":
      return 0;
    case "boolean":
      return false;
    case "string":
    default:
      if (schema.enum && schema.enum.length > 0) return schema.enum[0];
      return "";
  }
}

/** 把 schema.type 翻译成 input type 与展示文本。 */
function inputKindForType(schema) {
  if (!schema) return { kind: "text", label: "string" };
  const type = schema.type || "string";
  if (type === "boolean") return { kind: "checkbox", label: "boolean" };
  if (type === "integer" || type === "number") return { kind: "number", label: type };
  return { kind: "text", label: type };
}

/** 把 OpenAPI 的 paths 节点展开成扁平的 operation 列表。 */
function extractOperations(spec) {
  const operations = [];
  const paths = spec.paths || {};
  Object.keys(paths).forEach(function (path) {
    const pathItem = paths[path] || {};
    ["get", "post", "put", "delete", "patch", "options", "head"].forEach(function (method) {
      const op = pathItem[method];
      if (!op) return;
      const parameters = (op.parameters || []).filter(function (p) { return p.in === "query" || p.in === "path"; });
      let bodySchema = null;
      if (op.requestBody && op.requestBody.content) {
        const json = op.requestBody.content["application/json"];
        if (json && json.schema) bodySchema = json.schema;
      }
      operations.push({
        method: method.toUpperCase(),
        path: path,
        summary: op.summary || "",
        description: op.description || "",
        tags: op.tags || [],
        parameters: parameters,
        bodySchema: bodySchema
      });
    });
  });
  return operations;
}

/** 选择分组：优先用第一条匹配的 groupTitleByPrefix 规则，OpenAPI tag 留作兜底。 */
function pickGroup(op) {
  for (const rule of groupTitleByPrefix) {
    if (rule.match(op)) return rule.title;
  }
  if (op.tags && op.tags.length > 0) return op.tags[0];
  return "其它";
}

/** localStorage key 必须避免冲突，使用 method:path 复合键。 */
function storageKey(op) {
  return STORAGE_PREFIX + op.method + ":" + op.path;
}

/** 返回指定端点的手工调试样例，处理 JsonElement 等 OpenAPI 无法自动推断字段的接口。 */
function getRequestBodySample(op) {
  return requestBodySamples[op.method + " " + op.path] || null;
}

/** 读取本端点最近一次输入；解析失败则当作空。 */
function loadInputs(op) {
  try {
    const raw = window.localStorage.getItem(storageKey(op));
    return raw ? JSON.parse(raw) : null;
  } catch (e) {
    return null;
  }
}

/** 保存本端点最近一次输入；写入失败时静默忽略，避免影响调试体验。 */
function saveInputs(op, payload) {
  try {
    window.localStorage.setItem(storageKey(op), JSON.stringify(payload));
  } catch (e) {
    // localStorage 可能被禁用或满载，忽略写入失败。
  }
}

/** 拼接最终请求 URL（含 query 串与 path 参数替换）。 */
function buildRequestUrl(op, paramValues) {
  let path = op.path;
  const queryPairs = [];
  op.parameters.forEach(function (param) {
    const raw = paramValues[param.name];
    if (raw === undefined || raw === null || raw === "") return;
    if (param.in === "path") {
      path = path.replace("{" + param.name + "}", encodeURIComponent(raw));
    } else if (param.in === "query") {
      queryPairs.push(encodeURIComponent(param.name) + "=" + encodeURIComponent(raw));
    }
  });
  return path + (queryPairs.length > 0 ? "?" + queryPairs.join("&") : "");
}

/** 生成与浏览器请求等价的 curl 命令，便于复制到终端复现。 */
function buildCurlCommand(op, requestUrl, body) {
  const parts = ["curl", "-X", op.method, JSON.stringify(window.location.origin + requestUrl)];
  if (body !== null && body !== undefined && body !== "") {
    parts.push("-H", "\"Content-Type: application/json\"");
    parts.push("--data-raw", JSON.stringify(body));
  }
  return parts.join(" ");
}

/** 渲染参数输入表单，返回收集函数。 */
function renderParameterRows(container, op, savedValues) {
  if (op.parameters.length === 0) return function () { return {}; };

  const inputs = {};
  op.parameters.forEach(function (param) {
    const row = document.createElement("div");
    row.className = "form-row";

    const nameNode = document.createElement("div");
    nameNode.className = "name";
    nameNode.textContent = param.name + " (" + param.in + ")";
    if (param.required) {
      const requiredMark = document.createElement("span");
      requiredMark.className = "required";
      requiredMark.textContent = "*";
      nameNode.appendChild(requiredMark);
    }
    row.appendChild(nameNode);

    const kind = inputKindForType(param.schema);
    const inputNode = document.createElement("input");
    inputNode.type = kind.kind;
    if (kind.kind === "checkbox") {
      inputNode.checked = savedValues && Object.prototype.hasOwnProperty.call(savedValues, param.name)
        ? Boolean(savedValues[param.name])
        : Boolean(param.schema && param.schema.default);
    } else {
      let initial = "";
      if (savedValues && Object.prototype.hasOwnProperty.call(savedValues, param.name)) {
        initial = String(savedValues[param.name]);
      } else if (param.schema && param.schema.default !== undefined) {
        initial = String(param.schema.default);
      }
      inputNode.value = initial;
      if (kind.kind === "number") inputNode.step = "any";
    }
    row.appendChild(inputNode);

    const typeNode = document.createElement("div");
    typeNode.className = "type";
    typeNode.textContent = kind.label;
    row.appendChild(typeNode);

    container.appendChild(row);
    inputs[param.name] = { node: inputNode, kind: kind.kind, schema: param.schema };
  });

  return function collect() {
    const collected = {};
    Object.keys(inputs).forEach(function (key) {
      const item = inputs[key];
      if (item.kind === "checkbox") {
        collected[key] = item.node.checked;
      } else {
        const raw = item.node.value;
        if (raw === "") {
          collected[key] = "";
        } else if (item.kind === "number") {
          const num = Number(raw);
          collected[key] = Number.isNaN(num) ? raw : num;
        } else {
          collected[key] = raw;
        }
      }
    });
    return collected;
  };
}

/** 渲染请求体编辑器，返回收集函数。 */
function renderBodyEditor(container, op, savedBody) {
  if (!op.bodySchema) return function () { return null; };

  const block = document.createElement("div");
  block.className = "body-block";

  const labelRow = document.createElement("div");
  labelRow.className = "body-label";
  const left = document.createElement("div");
  left.className = "left";
  left.textContent = "请求体 (application/json)";
  labelRow.appendChild(left);

  const formatBtn = document.createElement("button");
  formatBtn.type = "button";
  formatBtn.className = "secondary";
  formatBtn.textContent = "格式化 JSON";
  labelRow.appendChild(formatBtn);

  block.appendChild(labelRow);

  const textarea = document.createElement("textarea");
  textarea.className = "body-editor";
  textarea.spellcheck = false;
  let initialText;
  if (savedBody !== undefined && savedBody !== null) {
    initialText = typeof savedBody === "string" ? savedBody : JSON.stringify(savedBody, null, 2);
  } else {
    const sample = getRequestBodySample(op) || buildSampleFromSchema(op.bodySchema, 0);
    initialText = sample === null ? "" : JSON.stringify(sample, null, 2);
  }
  textarea.value = initialText;
  block.appendChild(textarea);

  formatBtn.addEventListener("click", function () {
    try {
      const parsed = JSON.parse(textarea.value || "null");
      textarea.value = parsed === null ? "" : JSON.stringify(parsed, null, 2);
    } catch (e) {
      window.alert("JSON 解析失败: " + e.message);
    }
  });

  container.appendChild(block);

  return function collect() {
    return textarea.value;
  };
}

/** 把 HTTP 状态码翻译成颜色徽标 class。 */
function statusBadgeClass(status) {
  if (status >= 200 && status < 300) return "s2xx";
  if (status >= 300 && status < 400) return "s3xx";
  if (status >= 400 && status < 500) return "s4xx";
  if (status >= 500) return "s5xx";
  return "error";
}

/** 把响应头展开成可读字符串。 */
function formatHeaders(headers) {
  const lines = [];
  headers.forEach(function (value, key) { lines.push(key + ": " + value); });
  return lines.join("\n");
}

/** 在历史面板顶部追加一条记录，超过上限则丢弃尾部。 */
function pushHistory(entry) {
  state.history.unshift(entry);
  if (state.history.length > HISTORY_LIMIT) state.history.length = HISTORY_LIMIT;
  renderHistory();
}

function renderHistory() {
  const list = document.getElementById("history-list");
  list.innerHTML = "";
  if (state.history.length === 0) {
    const empty = document.createElement("li");
    empty.textContent = "暂无调用记录";
    empty.style.color = "var(--muted)";
    empty.style.gridTemplateColumns = "1fr";
    list.appendChild(empty);
    return;
  }
  state.history.forEach(function (entry) {
    const li = document.createElement("li");
    const method = document.createElement("span");
    method.className = "h-method";
    method.textContent = entry.method;
    method.style.color = entry.method === "GET" ? "var(--get)" : entry.method === "POST" ? "var(--post)" : "var(--muted)";
    const path = document.createElement("span");
    path.className = "h-path";
    path.title = entry.url;
    path.textContent = entry.url;
    const meta = document.createElement("span");
    meta.style.color = "var(--muted)";
    meta.textContent = (entry.status || "ERR") + " · " + entry.elapsedMs + "ms";
    li.appendChild(method);
    li.appendChild(path);
    li.appendChild(meta);
    list.appendChild(li);
  });
}

/** 渲染单个端点的卡片。 */
function renderOperationCard(op) {
  const card = document.createElement("section");
  card.className = "card collapsed";

  const head = document.createElement("div");
  head.className = "card-head";

  const badge = document.createElement("span");
  badge.className = "badge " + (["GET", "POST", "PUT", "DELETE"].indexOf(op.method) >= 0 ? op.method : "OTHER");
  badge.textContent = op.method;
  head.appendChild(badge);

  const path = document.createElement("span");
  path.className = "card-path";
  path.textContent = op.path;
  head.appendChild(path);

  const summary = document.createElement("span");
  summary.className = "card-summary";
  summary.textContent = op.summary;
  summary.title = op.summary;
  head.appendChild(summary);

  const toggle = document.createElement("span");
  toggle.className = "card-toggle";
  toggle.textContent = "展开 ▾";
  head.appendChild(toggle);

  head.addEventListener("click", function () {
    const collapsed = card.classList.toggle("collapsed");
    toggle.textContent = collapsed ? "展开 ▾" : "收起 ▴";
  });
  card.appendChild(head);

  const body = document.createElement("div");
  body.className = "card-body";

  // 描述（来自 XML summary）独立成一段
  if (op.summary) {
    const desc = document.createElement("div");
    desc.style.color = "var(--muted)";
    desc.style.marginBottom = "10px";
    desc.style.fontSize = "13px";
    desc.textContent = op.summary;
    body.appendChild(desc);
  }

  const saved = loadInputs(op) || {};

  // 参数区
  let collectParams = function () { return {}; };
  if (op.parameters.length > 0) {
    const paramsContainer = document.createElement("div");
    paramsContainer.className = "params";
    body.appendChild(paramsContainer);
    collectParams = renderParameterRows(paramsContainer, op, saved.params);
  }

  // 请求体区
  const collectBody = renderBodyEditor(body, op, saved.body);

  // 操作按钮
  const buttonRow = document.createElement("div");
  buttonRow.className = "button-row";

  const sendBtn = document.createElement("button");
  sendBtn.type = "button";
  sendBtn.textContent = "发送";

  const resetBtn = document.createElement("button");
  resetBtn.type = "button";
  resetBtn.className = "secondary";
  resetBtn.textContent = "重置";

  const curlBtn = document.createElement("button");
  curlBtn.type = "button";
  curlBtn.className = "secondary";
  curlBtn.textContent = "复制 curl";

  buttonRow.appendChild(sendBtn);
  buttonRow.appendChild(resetBtn);
  buttonRow.appendChild(curlBtn);
  body.appendChild(buttonRow);

  // 响应面板
  const responseBlock = document.createElement("div");
  responseBlock.className = "response-block";
  responseBlock.style.display = "none";
  body.appendChild(responseBlock);

  function renderResponse(payload) {
    responseBlock.style.display = "block";
    responseBlock.innerHTML = "";

    const summaryRow = document.createElement("div");
    summaryRow.className = "response-summary";

    const statusBadge = document.createElement("span");
    statusBadge.className = "status-badge " + statusBadgeClass(payload.status || 0);
    statusBadge.textContent = payload.status ? payload.status + " " + (payload.statusText || "") : "请求失败";
    summaryRow.appendChild(statusBadge);

    const elapsed = document.createElement("span");
    elapsed.style.color = "var(--muted)";
    elapsed.textContent = payload.elapsedMs + " ms · " + payload.url;
    summaryRow.appendChild(elapsed);

    responseBlock.appendChild(summaryRow);

    if (payload.error) {
      const pre = document.createElement("pre");
      pre.className = "response-body";
      pre.textContent = payload.error;
      responseBlock.appendChild(pre);
      return;
    }

    const headersDetails = document.createElement("details");
    const headersSummary = document.createElement("summary");
    headersSummary.textContent = "响应头";
    headersDetails.appendChild(headersSummary);
    const headersPre = document.createElement("pre");
    headersPre.className = "response-headers";
    headersPre.textContent = payload.headers;
    headersDetails.appendChild(headersPre);
    responseBlock.appendChild(headersDetails);

    const bodyPre = document.createElement("pre");
    bodyPre.className = "response-body";
    bodyPre.textContent = payload.bodyText;
    responseBlock.appendChild(bodyPre);
  }

  sendBtn.addEventListener("click", async function () {
    sendBtn.disabled = true;
    const params = collectParams();
    const rawBody = collectBody();
    saveInputs(op, { params: params, body: rawBody });

    const requestUrl = buildRequestUrl(op, params);
    const init = { method: op.method, headers: {} };

    // 仅 POST/PUT/PATCH/DELETE 才认为可能携带 body；对没有 bodySchema 的方法直接跳过。
    const methodAllowsBody = ["POST", "PUT", "PATCH", "DELETE"].indexOf(op.method) >= 0;
    if (methodAllowsBody && op.bodySchema && rawBody !== null && rawBody !== undefined && rawBody !== "") {
      init.headers["Content-Type"] = "application/json";
      init.body = rawBody;
    }

    const startedAt = performance.now();
    try {
      const response = await fetch(requestUrl, init);
      const elapsedMs = Math.round(performance.now() - startedAt);
      const text = await response.text();
      const contentType = response.headers.get("content-type") || "";
      let bodyText = text;
      if (contentType.indexOf("application/json") >= 0) {
        try {
          bodyText = JSON.stringify(JSON.parse(text), null, 2);
        } catch (e) {
          bodyText = text;
        }
      }
      renderResponse({
        status: response.status,
        statusText: response.statusText,
        headers: formatHeaders(response.headers),
        bodyText: bodyText,
        url: requestUrl,
        elapsedMs: elapsedMs
      });
      pushHistory({ method: op.method, url: requestUrl, status: response.status, elapsedMs: elapsedMs });
    } catch (err) {
      const elapsedMs = Math.round(performance.now() - startedAt);
      renderResponse({
        error: String(err && err.message ? err.message : err),
        url: requestUrl,
        elapsedMs: elapsedMs
      });
      pushHistory({ method: op.method, url: requestUrl, status: 0, elapsedMs: elapsedMs });
    } finally {
      sendBtn.disabled = false;
    }
  });

  resetBtn.addEventListener("click", function () {
    try { window.localStorage.removeItem(storageKey(op)); } catch (e) { /* 忽略 */ }
    // 直接重新渲染当前卡片：替换原 DOM 节点。
    const refreshed = renderOperationCard(op);
    refreshed.classList.remove("collapsed");
    refreshed.querySelector(".card-toggle").textContent = "收起 ▴";
    card.parentNode.replaceChild(refreshed, card);
  });

  curlBtn.addEventListener("click", function () {
    const params = collectParams();
    const rawBody = collectBody();
    const requestUrl = buildRequestUrl(op, params);
    const methodAllowsBody = ["POST", "PUT", "PATCH", "DELETE"].indexOf(op.method) >= 0;
    const bodyForCurl = methodAllowsBody && op.bodySchema ? rawBody : null;
    const command = buildCurlCommand(op, requestUrl, bodyForCurl);
    if (navigator.clipboard && navigator.clipboard.writeText) {
      navigator.clipboard.writeText(command).then(function () {
        curlBtn.textContent = "已复制 ✓";
        window.setTimeout(function () { curlBtn.textContent = "复制 curl"; }, 1500);
      }).catch(function () {
        window.prompt("复制失败，手动复制：", command);
      });
    } else {
      window.prompt("复制失败，手动复制：", command);
    }
  });

  card.appendChild(body);
  return card;
}

/** 把 operation 列表按分组渲染到主区域。 */
function renderGroups(operations) {
  const root = document.getElementById("debug-console-app");
  root.innerHTML = "";
  if (operations.length === 0) {
    const empty = document.createElement("div");
    empty.className = "empty-hint";
    empty.textContent = "OpenAPI 文档中没有任何端点。";
    root.appendChild(empty);
    return;
  }

  const grouped = new Map();
  operations.forEach(function (op) {
    const groupTitle = pickGroup(op);
    if (!grouped.has(groupTitle)) grouped.set(groupTitle, []);
    grouped.get(groupTitle).push(op);
  });

  // 固定输出顺序：基础与状态在前，ControllerClient 兼容在后，其余按字典序。
  const orderedTitles = [];
  ["基础与状态", "ControllerClient 兼容"].forEach(function (title) {
    if (grouped.has(title)) orderedTitles.push(title);
  });
  Array.from(grouped.keys()).sort().forEach(function (title) {
    if (orderedTitles.indexOf(title) < 0) orderedTitles.push(title);
  });

  orderedTitles.forEach(function (title) {
    const ops = grouped.get(title);
    ops.sort(function (a, b) {
      if (a.path === b.path) return a.method.localeCompare(b.method);
      return a.path.localeCompare(b.path);
    });

    const section = document.createElement("section");
    section.className = "group";
    const heading = document.createElement("h2");
    heading.textContent = title + "  (" + ops.length + ")";
    section.appendChild(heading);
    ops.forEach(function (op) { section.appendChild(renderOperationCard(op)); });
    root.appendChild(section);
  });
}

/** 加载 OpenAPI 文档并渲染。 */
async function loadSpecAndRender() {
  const metaSpec = document.getElementById("meta-spec-url");
  const metaCount = document.getElementById("meta-operation-count");
  const metaStatus = document.getElementById("meta-status");

  metaSpec.textContent = "正在读取调试配置...";
  metaStatus.textContent = "正在拉取 OpenAPI 文档...";
  metaStatus.className = "";

  try {
    const configResponse = await fetch(DEBUG_CONFIG_URL, { cache: "no-store" });
    if (!configResponse.ok) throw new Error("调试配置 HTTP " + configResponse.status + " " + configResponse.statusText);
    const config = await configResponse.json();
    const swaggerJsonUrl = config.swaggerJsonUrl;
    if (!swaggerJsonUrl) throw new Error("调试配置缺少 swaggerJsonUrl");
    metaSpec.textContent = swaggerJsonUrl;

    const response = await fetch(swaggerJsonUrl, { cache: "no-store" });
    if (!response.ok) throw new Error("HTTP " + response.status + " " + response.statusText);
    const spec = await response.json();
    state.spec = spec;
    state.operations = extractOperations(spec);
    metaCount.textContent = state.operations.length;
    metaStatus.textContent = "已加载";
    metaStatus.className = "good";
    renderGroups(state.operations);
  } catch (err) {
    metaStatus.textContent = "加载失败: " + (err && err.message ? err.message : err);
    metaStatus.className = "bad";
    metaCount.textContent = "0";
    const root = document.getElementById("debug-console-app");
    root.innerHTML = "";
    const errBlock = document.createElement("div");
    errBlock.className = "empty-hint";
    errBlock.textContent = "无法加载 OpenAPI 文档，请确认 Swagger:Enabled = true 且 " + DEBUG_CONFIG_URL + " 可访问。";
    root.appendChild(errBlock);
  }
}

document.getElementById("reload-spec").addEventListener("click", loadSpecAndRender);
document.getElementById("history-clear").addEventListener("click", function () {
  state.history.length = 0;
  renderHistory();
});

renderHistory();
loadSpecAndRender();
