import { computed, ref } from "vue";

const bootstrap = ref<any>(null);
const releases = ref<any>(null);
const sources = ref<any>(null);
const notices = ref<any[]>([]);
const loading = ref(false);
const error = ref("");
const loadedAt = ref("");
const requestState = ref<Record<string, "idle" | "loading" | "ready" | "error">>({
  bootstrap: "idle",
  releases: "idle",
  sources: "idle",
  notices: "idle",
});
let loaded = false;

const endpointLabels: Record<string, string> = {
  "/api/client/bootstrap": "客户端启动配置",
  "/api/client/releases": "发布信息",
  "/api/client/sources": "接口源目录",
  "/api/client/notices": "版本日志",
};

async function fetchJSON(path: string) {
  let res: Response;
  try {
    res = await fetch(path, { headers: { Accept: "application/json" } });
  } catch {
    throw new Error(`${endpointLabels[path] || path} 暂时无法连接`);
  }
  if (!res.ok) throw new Error(`${endpointLabels[path] || path} 返回 HTTP ${res.status}`);
  try {
    return await res.json();
  } catch {
    throw new Error(`${endpointLabels[path] || path} 返回内容不是有效 JSON`);
  }
}

function failureMessage(reason: unknown) {
  return reason instanceof Error ? reason.message : String(reason || "读取失败");
}

export function usePortalState() {
  const packages = computed(() => releases.value?.packages || bootstrap.value?.release?.packages || []);
  const categories = computed(() => sources.value?.categories || bootstrap.value?.sources?.categories || []);
  const latestNotice = computed(() => notices.value[0] || releases.value?.latest_notice || bootstrap.value?.release?.latest_notice || null);
  const sourceCount = computed(() => categories.value.reduce((total: number, cat: any) => total + (cat.subcategories?.length || 0), 0));
  const healthyCount = computed(() => categories.value.reduce((total: number, cat: any) => {
    return total + (cat.subcategories || []).filter((item: any) => sourceStatus(item) === "ok").length;
  }, 0));
  const availability = computed(() => sourceCount.value ? Math.round((healthyCount.value / sourceCount.value) * 100) : 0);
  const downloadUrl = computed(() => releases.value?.download_url || bootstrap.value?.release?.download_url || packages.value[0]?.url || "");
  const appVersion = computed(() => releases.value?.app_version || bootstrap.value?.release?.app_version || latestNotice.value?.version || "未发布");
  const serviceVersion = computed(() => bootstrap.value?.serviceVersion || "-");
  const branding = computed(() => ({
    siteIconUrl: bootstrap.value?.branding?.siteIconUrl || "https://img.ymhut.cn/file/1782108850041_icon.webp",
    developerAvatarUrl: bootstrap.value?.branding?.developerAvatarUrl || "https://img.ymhut.cn/file/1782108780690_b_3db45f3787f19192c8de8e06bc0987ef.webp",
    developerName: bootstrap.value?.branding?.developerName || "YMhut",
    feedbackEmail: bootstrap.value?.branding?.feedbackEmail || "support@ymhut.cn",
  }));
  const isReady = computed(() => loaded && !loading.value && !error.value);
  const hasPartialData = computed(() => Boolean(bootstrap.value || releases.value || sources.value || notices.value.length));
  const releasesEmpty = computed(() => !loading.value && packages.value.length === 0 && notices.value.length === 0);
  const sourcesEmpty = computed(() => !loading.value && categories.value.length === 0);

  async function load(force = false) {
    if (loaded && !force) return;
    loading.value = true;
    error.value = "";
    requestState.value = { bootstrap: "loading", releases: "loading", sources: "loading", notices: "loading" };
    try {
      const [bootstrapData, releaseData, sourceData, noticeData] = await Promise.allSettled([
        fetchJSON("/api/client/bootstrap"),
        fetchJSON("/api/client/releases"),
        fetchJSON("/api/client/sources"),
        fetchJSON("/api/client/notices"),
      ]);
      if (bootstrapData.status === "fulfilled") {
        bootstrap.value = bootstrapData.value;
        requestState.value.bootstrap = "ready";
      } else {
        requestState.value.bootstrap = "error";
      }
      if (releaseData.status === "fulfilled") {
        releases.value = releaseData.value;
        requestState.value.releases = "ready";
      } else {
        requestState.value.releases = "error";
      }
      if (sourceData.status === "fulfilled") {
        sources.value = sourceData.value;
        requestState.value.sources = "ready";
      } else {
        requestState.value.sources = "error";
      }
      if (noticeData.status === "fulfilled") {
        notices.value = noticeData.value.items || [];
        requestState.value.notices = "ready";
      } else {
        requestState.value.notices = "error";
      }
      const firstFailure = [bootstrapData, releaseData, sourceData, noticeData].find((item) => item.status === "rejected") as PromiseRejectedResult | undefined;
      if (firstFailure && !hasPartialData.value) error.value = failureMessage(firstFailure.reason);
      loaded = true;
      loadedAt.value = new Date().toISOString();
    } catch (err) {
      error.value = failureMessage(err);
    } finally {
      loading.value = false;
    }
  }

  return {
    bootstrap,
    releases,
    sources,
    notices,
    loading,
    error,
    loadedAt,
    requestState,
    packages,
    categories,
    latestNotice,
    sourceCount,
    healthyCount,
    availability,
    downloadUrl,
    appVersion,
    serviceVersion,
    branding,
    isReady,
    hasPartialData,
    releasesEmpty,
    sourcesEmpty,
    load,
    sourceStatus,
    statusTone,
    formatBytes,
  };
}

export function sourceStatus(item: any) {
  return item.health?.status || item.lastStatus || "unknown";
}

export function statusTone(status: string) {
  const value = String(status || "").toLowerCase();
  if (["ok", "redirected", "sqlite", "mysql", "online", "ready"].includes(value)) return "good";
  if (["degraded", "pending", "missing"].includes(value)) return "warn";
  if (["error", "offline", "failed"].includes(value)) return "bad";
  return "neutral";
}

export function formatBytes(value: number) {
  if (!Number.isFinite(value) || value <= 0) return "0 B";
  const units = ["B", "KB", "MB", "GB"];
  let next = value;
  let index = 0;
  while (next >= 1024 && index < units.length - 1) {
    next /= 1024;
    index += 1;
  }
  return `${next.toFixed(index === 0 ? 0 : 1)} ${units[index]}`;
}
