// Good Go Global live API bridge for the Cloudflare Worker backend.

const GGG_API_STATE = {
  user: null,
  snapshot: null,
  project: null,
  report: null,
  currentProjectId: localStorage.getItem("ggg_active_project_id") || "",
  busy: false,
  message: "",
  error: "",
  authChecked: false,
};

function emitGGGState(patch = {}) {
  Object.assign(GGG_API_STATE, patch);
  window.dispatchEvent(new CustomEvent("ggg:state", { detail: GGG_API_STATE }));
}

function i18nText(key, values) {
  return window.GGGI18n ? window.GGGI18n.t(key, values) : key;
}

function errorText(error) {
  if (!error) return "";
  if (error.code === "unauthorized") return i18nText("请先登录。");
  const codeMap = {
    invalid_email: "请输入有效邮箱地址。",
    password_too_short: "密码至少需要 8 位。",
    password_too_weak: "密码需要同时包含字母和数字。",
    email_not_verified: "邮箱尚未验证",
    email_provider_not_configured: "邮箱验证服务尚未配置，无法创建新账号。",
    invalid_credentials: "登录失败，请检查邮箱和密码。"
  };
  return i18nText(codeMap[error.code] || error.message || String(error));
}

function getActiveProjectId() {
  return GGG_API_STATE.currentProjectId || localStorage.getItem("ggg_active_project_id") || "";
}

function resolveActiveProject(snapshot = GGG_API_STATE.snapshot, preferredId = "") {
  const projects = snapshot?.projects || [];
  if (!projects.length) return null;
  const activeId = preferredId || getActiveProjectId();
  return projects.find((project) => project.id === activeId) || projects[0] || null;
}

function setActiveProjectId(projectId) {
  const nextId = projectId || "";
  if (nextId) localStorage.setItem("ggg_active_project_id", nextId);
  else localStorage.removeItem("ggg_active_project_id");
  const project = resolveActiveProject(GGG_API_STATE.snapshot, nextId);
  emitGGGState({ currentProjectId: nextId, project });
  return project;
}

async function gggRequest(path, options = {}) {
  const headers = new Headers(options.headers || {});
  const isFormData = typeof FormData !== "undefined" && options.body instanceof FormData;
  if (options.body && !isFormData && !headers.has("Content-Type")) headers.set("Content-Type", "application/json");
  const response = await fetch(path, { ...options, headers, credentials: "same-origin" });
  const contentType = response.headers.get("content-type") || "";
  const body = contentType.includes("application/json") ? await response.json() : await response.blob();
  if (!response.ok) {
    const error = new Error(body?.message || body?.error || `${path} failed with ${response.status}`);
    error.code = body?.error || "";
    error.status = response.status;
    error.body = body;
    throw error;
  }
  return body;
}

async function getCurrentUser() {
  const me = await gggRequest("/api/auth/me");
  if (me.user) {
    emitGGGState({ user: me.user, authChecked: true });
    return me.user;
  }
  emitGGGState({ user: null, snapshot: null, authChecked: true });
  return null;
}

async function loadAppSnapshot() {
  emitGGGState({ busy: true, message: i18nText("正在从 D1 加载工作台数据..."), error: "" });
  try {
    const user = await getCurrentUser();
    if (!user) {
      const error = new Error(i18nText("请先登录。"));
      error.code = "unauthorized";
      throw error;
    }
    const snapshot = await gggRequest("/api/app/snapshot");
    const activeProject = resolveActiveProject(snapshot);
    if (activeProject?.id && !getActiveProjectId()) localStorage.setItem("ggg_active_project_id", activeProject.id);
    emitGGGState({
      user: snapshot.user,
      snapshot,
      project: activeProject,
      currentProjectId: activeProject?.id || "",
      report: snapshot.reports?.find((report) => report.project_id === activeProject?.id && report.report_type === "main") || snapshot.latestReport || null,
      busy: false,
      message: i18nText("D1 数据已加载")
    });
    return snapshot;
  } catch (error) {
    emitGGGState({ busy: false, error: error.code === "unauthorized" ? "" : errorText(error) });
    throw error;
  }
}

function useGGGSnapshot() {
  const [state, setState] = React.useState(GGG_API_STATE);
  React.useEffect(() => {
    const listener = (event) => setState({ ...event.detail });
    window.addEventListener("ggg:state", listener);
    if (!GGG_API_STATE.snapshot && !GGG_API_STATE.busy) {
      loadAppSnapshot().catch(() => {});
    }
    return () => window.removeEventListener("ggg:state", listener);
  }, []);
  return {
    snapshot: state.snapshot,
    project: state.project,
    report: state.report,
    currentProjectId: state.currentProjectId,
    user: state.user,
    busy: state.busy,
    error: state.error,
    authChecked: state.authChecked,
    reload: loadAppSnapshot
  };
}

function parseJson(value, fallback) {
  try { return value ? JSON.parse(value) : fallback; } catch { return fallback; }
}

async function login(email, password) {
  emitGGGState({ busy: true, message: i18nText("正在登录..."), error: "" });
  try {
    const data = await gggRequest("/api/auth/login", {
      method: "POST",
      body: JSON.stringify({ email, password }),
    });
    emitGGGState({ user: data.user, snapshot: null, busy: false, message: i18nText("登录成功") });
    const snapshot = await loadAppSnapshot();
    return { user: data.user, snapshot };
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
    throw error;
  }
}

async function registerAccount(payload) {
  emitGGGState({ busy: true, message: i18nText("正在创建账户并发送邮箱验证..."), error: "" });
  try {
    const data = await gggRequest("/api/auth/register", {
      method: "POST",
      body: JSON.stringify(payload),
    });
    emitGGGState({ busy: false, message: i18nText(data.message || "验证邮件已发送") });
    return data;
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
    throw error;
  }
}

async function resendVerification(email, password) {
  emitGGGState({ busy: true, message: i18nText("正在重新发送验证邮件..."), error: "" });
  try {
    const data = await gggRequest("/api/auth/resend-verification", {
      method: "POST",
      body: JSON.stringify({ email, password }),
    });
    emitGGGState({ busy: false, message: i18nText(data.message || "验证邮件已重新发送") });
    return data;
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
    throw error;
  }
}

async function saveCompanyProfile(profile) {
  emitGGGState({ busy: true, message: i18nText("正在保存公司信息..."), error: "" });
  try {
    const data = await gggRequest("/api/company-profile", {
      method: "PUT",
      body: JSON.stringify(profile),
    });
    emitGGGState({ user: data.user, snapshot: null, busy: false, message: i18nText("公司信息已保存") });
    await loadAppSnapshot();
    return data;
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
    throw error;
  }
}

async function saveAccountProfile(profile) {
  emitGGGState({ busy: true, message: i18nText("正在保存账户资料..."), error: "" });
  try {
    const data = await gggRequest("/api/account/profile", {
      method: "PATCH",
      body: JSON.stringify(profile),
    });
    emitGGGState({ user: data.user, snapshot: null, busy: false, message: i18nText("账户资料已保存") });
    await loadAppSnapshot();
    return data;
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
    throw error;
  }
}

async function saveAccountCompany(company) {
  emitGGGState({ busy: true, message: i18nText("正在保存企业资料..."), error: "" });
  try {
    const data = await gggRequest("/api/account/company", {
      method: "PATCH",
      body: JSON.stringify(company),
    });
    emitGGGState({ user: data.user, snapshot: null, busy: false, message: i18nText("企业资料已保存") });
    await loadAppSnapshot();
    return data;
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
    throw error;
  }
}

async function saveAccountPreferences(preferences) {
  emitGGGState({ busy: true, message: i18nText("正在保存语言与报告偏好..."), error: "" });
  try {
    const data = await gggRequest("/api/account/preferences", {
      method: "PATCH",
      body: JSON.stringify(preferences),
    });
    emitGGGState({ user: data.user, snapshot: null, busy: false, message: i18nText("语言与报告偏好已保存") });
    await loadAppSnapshot();
    return data;
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
    throw error;
  }
}

async function uploadAvatar(file) {
  const form = new FormData();
  form.append("avatar", file);
  emitGGGState({ busy: true, message: i18nText("正在上传头像..."), error: "" });
  try {
    const data = await gggRequest("/api/account/avatar", {
      method: "POST",
      body: form,
    });
    emitGGGState({ user: data.user, snapshot: null, busy: false, message: i18nText("头像已保存") });
    await loadAppSnapshot();
    return data;
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
    throw error;
  }
}

async function markDashboardTourSeen() {
  const data = await gggRequest("/api/onboarding/dashboard-tour/seen", { method: "POST", body: "{}" });
  emitGGGState({ user: data.user || GGG_API_STATE.user });
  return data;
}

async function createProject(payload) {
  const created = await gggRequest("/api/projects", {
    method: "POST",
    body: JSON.stringify(payload),
  });
  if (created.project?.id) setActiveProjectId(created.project.id);
  emitGGGState({ project: created.project, currentProjectId: created.project?.id || getActiveProjectId() });
  await loadAppSnapshot();
  return created.project;
}

async function updateProject(projectId, payload) {
  const updated = await gggRequest(`/api/projects/${projectId}`, {
    method: "PATCH",
    body: JSON.stringify(payload),
  });
  if (updated.project?.id) setActiveProjectId(updated.project.id);
  emitGGGState({ project: updated.project, currentProjectId: updated.project?.id || getActiveProjectId() });
  await loadAppSnapshot();
  return updated.project;
}

async function deleteProject(projectId) {
  await gggRequest(`/api/projects/${projectId}`, { method: "DELETE" });
  if (getActiveProjectId() === projectId) setActiveProjectId("");
  emitGGGState({ project: null, report: null });
  const snapshot = await loadAppSnapshot();
  const nextProject = resolveActiveProject(snapshot);
  if (nextProject?.id) setActiveProjectId(nextProject.id);
  return true;
}

async function uploadProjectFilesDetailed(projectId, files) {
  if (!projectId) throw new Error(i18nText("请先创建真实项目，再上传资料。"));
  const form = new FormData();
  Array.from(files || []).forEach((file) => form.append("files", file));
  emitGGGState({ busy: true, message: i18nText("正在上传资料..."), error: "" });
  try {
    const data = await gggRequest(`/api/projects/${projectId}/files`, {
      method: "POST",
      body: form,
    });
    emitGGGState({ busy: false, message: i18nText("资料已保存") });
    await loadAppSnapshot();
    return data;
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
    throw error;
  }
}

async function uploadProjectFiles(projectId, files) {
  const data = await uploadProjectFilesDetailed(projectId, files);
  return data.files || [];
}

async function deleteProjectFile(fileId) {
  await gggRequest(`/api/files/${fileId}`, { method: "DELETE" });
  await loadAppSnapshot();
  return true;
}

async function getFileAnalysis(fileId) {
  return gggRequest(`/api/files/${fileId}/analysis`);
}

async function listProjectTasks(projectId, filters = {}) {
  const targetProjectId = projectId || getActiveProjectId() || GGG_API_STATE.project?.id;
  if (!targetProjectId) throw new Error(i18nText("请先选择项目。"));
  const params = new URLSearchParams();
  if (filters.status && filters.status !== "all") params.set("status", filters.status);
  if (filters.priority && filters.priority !== "all") params.set("priority", filters.priority);
  const suffix = params.toString() ? `?${params.toString()}` : "";
  return gggRequest(`/api/projects/${targetProjectId}/tasks${suffix}`);
}

async function updateTask(taskId, patch = {}) {
  const data = await gggRequest(`/api/tasks/${taskId}`, {
    method: "PATCH",
    body: JSON.stringify(patch),
  });
  return data.task;
}

async function uploadTaskAttachment(taskId, files) {
  const form = new FormData();
  Array.from(files || []).forEach((file) => form.append("files", file));
  const data = await gggRequest(`/api/tasks/${taskId}/attachments`, {
    method: "POST",
    body: form,
  });
  return data.attachments || [];
}

async function analyzeTask(taskId, notes) {
  const data = await gggRequest(`/api/tasks/${taskId}/analyze`, {
    method: "POST",
    body: JSON.stringify({ notes }),
  });
  return data;
}

function downloadTaskAttachment(attachmentId) {
  window.location.href = `/api/task-attachments/${attachmentId}/download`;
}

async function recommendProjectGoals(projectId, draft = {}) {
  const targetProjectId = projectId || GGG_API_STATE.project?.id || GGG_API_STATE.snapshot?.projects?.[0]?.id;
  if (!targetProjectId) throw new Error(i18nText("请先创建真实项目，再生成报告。"));
  emitGGGState({ busy: true, message: i18nText("正在使用 OpenAI 推荐阶段目标..."), error: "" });
  try {
    const data = await gggRequest(`/api/projects/${targetProjectId}/goals/recommend`, {
      method: "POST",
      body: JSON.stringify(draft),
    });
    emitGGGState({ busy: false, message: i18nText("AI 目标建议已生成") });
    return data.goals || {};
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
    throw error;
  }
}

async function getReportSectionChat(sectionId) {
  return gggRequest(`/api/report-sections/${sectionId}/chat`);
}

async function askReportSectionQuestion(sectionId, question) {
  emitGGGState({ busy: true, message: i18nText("正在基于本节报告内容回答..."), error: "" });
  try {
    const data = await gggRequest(`/api/report-sections/${sectionId}/chat`, {
      method: "POST",
      body: JSON.stringify({ question }),
    });
    emitGGGState({ busy: false, message: i18nText("AI 助手已回答") });
    return data;
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
    throw error;
  }
}

function downloadProjectFile(fileId) {
  window.location.href = `/api/files/${fileId}/download`;
}

function downloadReport(reportId, format = "pdf") {
  if (!reportId) throw new Error(i18nText("当前项目暂无可下载报告。"));
  window.location.href = `/api/reports/${reportId}/export?format=${encodeURIComponent(format)}`;
}

async function getCreditPackages(region) {
  const suffix = region ? `?region=${encodeURIComponent(region)}` : "";
  return gggRequest(`/api/credits/packages${suffix}`);
}

async function listAdminCreditPackages() {
  return gggRequest("/api/admin/credits/packages");
}

async function updateAdminCreditPackage(packageId, payload) {
  const data = await gggRequest(`/api/admin/credits/packages/${encodeURIComponent(packageId)}`, {
    method: "PATCH",
    body: JSON.stringify(payload),
  });
  await loadAppSnapshot();
  return data.package;
}

async function listAdminUsers(params = {}) {
  const search = new URLSearchParams();
  Object.entries(params || {}).forEach(([key, value]) => {
    if (value !== undefined && value !== null && String(value).trim() !== "") search.set(key, value);
  });
  const suffix = search.toString() ? `?${search.toString()}` : "";
  return gggRequest(`/api/admin/users${suffix}`);
}

async function getAdminUser(userId) {
  return gggRequest(`/api/admin/users/${encodeURIComponent(userId)}`);
}

async function updateAdminUser(userId, payload) {
  return gggRequest(`/api/admin/users/${encodeURIComponent(userId)}`, {
    method: "PATCH",
    body: JSON.stringify(payload),
  });
}

async function createAdminPasswordReset(userId, mode = "link") {
  return gggRequest(`/api/admin/users/${encodeURIComponent(userId)}/password-reset`, {
    method: "POST",
    body: JSON.stringify({ mode }),
  });
}

async function validatePasswordResetToken(token) {
  return gggRequest(`/api/auth/password-reset/validate?token=${encodeURIComponent(token)}`);
}

async function completePasswordReset(token, password) {
  return gggRequest("/api/auth/password-reset/complete", {
    method: "POST",
    body: JSON.stringify({ token, password }),
  });
}

async function getCreditWallet() {
  const workspaceId = GGG_API_STATE.snapshot?.credits?.workspace?.id || GGG_API_STATE.snapshot?.workspace?.id || "default";
  return gggRequest(`/api/workspaces/${workspaceId}/credits/wallet`);
}

async function createCreditOrder(packageId, provider = "mock") {
  emitGGGState({ busy: true, message: i18nText("正在创建点数订单..."), error: "" });
  try {
    const order = await gggRequest("/api/credits/orders", {
      method: "POST",
      headers: { "Idempotency-Key": `order:${packageId}:${Date.now()}` },
      body: JSON.stringify({ packageId, provider }),
    });
    await loadAppSnapshot();
    emitGGGState({ busy: false, message: i18nText("点数已入账") });
    return order;
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
    throw error;
  }
}

async function quoteDeliverable(projectId, deliverableSkuId = "standard_report_80", topic = "main") {
  return gggRequest("/api/deliverables/quote", {
    method: "POST",
    body: JSON.stringify({ projectId, deliverableSkuId, topic }),
  });
}

async function acceptDeliverableQuote(quoteId) {
  return gggRequest(`/api/deliverables/${quoteId}/accept`, {
    method: "POST",
    headers: { "Idempotency-Key": `accept:${quoteId}` },
    body: "{}",
  });
}

async function getGenerationJob(jobId) {
  return gggRequest(`/api/generation-jobs/${jobId}`);
}

function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function waitForGenerationJob(jobId, { timeoutMs = 120000 } = {}) {
  const started = Date.now();
  let attempt = 0;
  while (Date.now() - started < timeoutMs) {
    const payload = await getGenerationJob(jobId);
    const job = payload.job || {};
    const progress = parseJson(job.progress_json, {});
    if (progress.message) emitGGGState({ busy: true, message: progress.message });
    if (job.status === "completed") return payload;
    if (job.status === "failed") {
      const errorInfo = parseJson(job.error_json, {});
      throw new Error(errorInfo.message || progress.message || "报告生成失败，点数预留已释放。");
    }
    attempt += 1;
    await delay(Math.min(1000 + attempt * 400, 3500));
  }
  throw new Error("报告生成仍在进行中，请稍后在报告列表中查看结果。");
}

async function getReportById(reportId) {
  const payload = await gggRequest(`/api/reports/${reportId}`);
  if (payload.report) emitGGGState({ report: payload.report });
  return payload;
}

async function generateReportForProject(projectId) {
  const targetProjectId = projectId || GGG_API_STATE.project?.id || GGG_API_STATE.snapshot?.projects?.[0]?.id;
  if (!targetProjectId) throw new Error(i18nText("请先创建真实项目，再生成报告。"));
  emitGGGState({ busy: true, message: i18nText("正在报价：标准报告需要 80 点，失败不扣点..."), error: "" });
  try {
    await gggRequest(`/api/projects/${targetProjectId}/ai-questions/generate`, { method: "POST", body: "{}" });
    const quoted = await quoteDeliverable(targetProjectId, "standard_report_80", "main");
    const available = Number(quoted.wallet?.available_credits || GGG_API_STATE.snapshot?.credits?.wallet?.available_credits || 0);
    const payable = Number(quoted.quote?.payable_credits || 0);
    if (payable > available) {
      throw new Error(`好出海点数不足：需要 ${payable} 点，当前可用 ${available} 点。请先到「点数中心」充值。`);
    }
    emitGGGState({ busy: true, message: i18nText("已确认报价，正在预留点数并创建生成任务..."), error: "" });
    const accepted = await acceptDeliverableQuote(quoted.quote.id);
    emitGGGState({ busy: true, message: i18nText("正在生成报告：AI 将调用资料搜集、Prompt Skill 和真实来源校验..."), error: "" });
    const generated = await gggRequest(`/api/projects/${targetProjectId}/reports/generate-v2`, {
      method: "POST",
      body: JSON.stringify({
        async: true,
        wait: false,
        language: "zh-CN",
        quoteId: quoted.quote.id,
        jobId: accepted.job?.id,
        deliverableSkuId: "standard_report_80"
      }),
    });
    let report = generated.report;
    const jobId = generated.billing?.jobId || accepted.job?.id;
    if (!report && jobId) {
      emitGGGState({ busy: true, message: i18nText("生成任务已创建，正在轮询 Job/Step 进度...") });
      const completed = await waitForGenerationJob(jobId);
      const artifactId = completed.job?.result_artifact_id || completed.job?.artifact_id || completed.job?.artifactId;
      if (!artifactId) throw new Error("生成任务完成但缺少报告 ID。");
      const reportPayload = await getReportById(artifactId);
      report = reportPayload.report;
    }
    emitGGGState({ report, busy: false, message: i18nText("报告已生成，80 点已在质量通过后实扣，可重复下载") });
    await loadAppSnapshot();
    return report;
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
    throw error;
  }
}

async function ensureReport() {
  const activeProject = resolveActiveProject();
  const report = GGG_API_STATE.report
    || GGG_API_STATE.snapshot?.reports?.find((item) => item.project_id === activeProject?.id && (item.report_type || "main") === "main")
    || GGG_API_STATE.snapshot?.latestReport;
  if (!report?.id) throw new Error(i18nText("当前账号暂无报告，请先基于真实项目生成报告。"));
  emitGGGState({ report });
  return report;
}

async function downloadLatest(format) {
  emitGGGState({ busy: true, message: `${i18nText("准备下载")} ${format.toUpperCase()}...`, error: "" });
  try {
    const report = await ensureReport();
    downloadReport(report.id, format);
    emitGGGState({ busy: false, message: `${format.toUpperCase()} ${i18nText("下载已开始")}` });
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
  }
}

async function checkout(provider, plan = "pro") {
  emitGGGState({ busy: true, message: `${i18nText("正在创建订阅会话")} ${provider}...`, error: "" });
  try {
    await getCurrentUser();
    const session = await gggRequest("/api/billing/checkout", {
      method: "POST",
      body: JSON.stringify({ provider, plan }),
    });
    if (session.checkoutUrl) {
      window.open(session.checkoutUrl, "_blank", "noopener");
    } else {
      alert(`${provider} ${i18nText("支付会话已创建")}：${session.status}\n${session.providerPayload?.message || ""}`);
    }
    emitGGGState({ busy: false, message: `${provider} ${i18nText("订阅会话已创建")}` });
    return session;
  } catch (error) {
    emitGGGState({ busy: false, error: errorText(error) });
    throw error;
  }
}

async function logout() {
  await gggRequest("/api/auth/logout", { method: "POST", body: "{}" });
  emitGGGState({ user: null, snapshot: null, project: null, report: null, busy: false, message: i18nText("已退出登录"), error: "" });
}

function LiveStatusDock() {
  const [state, setState] = React.useState(GGG_API_STATE);
  React.useEffect(() => {
    const listener = (event) => setState({ ...event.detail });
    window.addEventListener("ggg:state", listener);
    gggRequest("/api/health")
      .then((health) => emitGGGState({ message: `${i18nText("status.worker")} · D1 ${health.d1?.connected ? i18nText("status.connected") : i18nText("status.disconnected")} · OpenAI ${health.openaiConfigured ? i18nText("status.configured") : i18nText("status.notConfigured")} · Resend ${health.resendConfigured ? i18nText("status.configured") : i18nText("status.notConfigured")}` }))
      .catch((error) => emitGGGState({ error: errorText(error) }));
    return () => window.removeEventListener("ggg:state", listener);
  }, []);

  if (!state.message && !state.error && !state.busy) return null;
  return (
    <div className={"live-dock " + (state.error ? "error" : state.busy ? "busy" : "")}>
      <div>
        <b>{state.busy ? i18nText("status.processing") : state.error ? i18nText("status.needsAction") : i18nText("status.system")}</b>
        <span>{state.error || state.message}</span>
      </div>
      <button onClick={() => emitGGGState({ message: "", error: "", busy: false })}>
        <Icon name="x" size={14} />
      </button>
    </div>
  );
}

window.GGGApi = {
  request: gggRequest,
  getActiveProjectId,
  setActiveProjectId,
  resolveActiveProject,
  getCurrentUser,
  loadAppSnapshot,
  login,
  registerAccount,
  resendVerification,
  saveCompanyProfile,
  saveAccountProfile,
  saveAccountCompany,
  saveAccountPreferences,
  uploadAvatar,
  markDashboardTourSeen,
  createProject,
  updateProject,
  deleteProject,
  uploadProjectFilesDetailed,
  uploadProjectFiles,
  deleteProjectFile,
  getFileAnalysis,
  listProjectTasks,
  updateTask,
  uploadTaskAttachment,
  analyzeTask,
  downloadTaskAttachment,
  recommendProjectGoals,
  getCreditPackages,
  listAdminCreditPackages,
  updateAdminCreditPackage,
  listAdminUsers,
  getAdminUser,
  updateAdminUser,
  createAdminPasswordReset,
  validatePasswordResetToken,
  completePasswordReset,
  getCreditWallet,
  createCreditOrder,
  quoteDeliverable,
  acceptDeliverableQuote,
  getGenerationJob,
  waitForGenerationJob,
  getReportById,
  getReportSectionChat,
  askReportSectionQuestion,
  downloadProjectFile,
  downloadReport,
  generateReportForProject,
  downloadLatest,
  checkout,
  logout,
};
window.LiveStatusDock = LiveStatusDock;
window.useGGGSnapshot = useGGGSnapshot;
window.parseJson = parseJson;
