import { focusManager as z } from "./focusManager.js";
import { environmentManager as L } from "./environmentManager.js";
import { notifyManager as B } from "./notifyManager.js";
import { fetchState as H } from "./query.js";
import { Subscribable as V } from "./subscribable.js";
import { pendingThenable as k } from "./thenable.js";
import { resolveEnabled as o, shallowEqualObjects as C, resolveStaleTime as d, noop as K, isValidTimeout as A, timeUntilStale as N, replaceData as _ } from "./utils.js";
import { timeoutManager as R } from "./timeoutManager.js";
var rt = class extends V {
  constructor(t, e) {
    super(), this.options = e, this.#s = t, this.#r = null, this.#i = k(), this.bindMethods(), this.setOptions(e);
  }
  #s;
  #t = void 0;
  #p = void 0;
  #e = void 0;
  #a;
  #c;
  #i;
  #r;
  #b;
  #u;
  // This property keeps track of the last query with defined data.
  // It will be used to pass the previous data and query to the placeholder function between renders.
  #d;
  #h;
  #o;
  #n;
  #f = /* @__PURE__ */ new Set();
  bindMethods() {
    this.refetch = this.refetch.bind(this);
  }
  onSubscribe() {
    this.listeners.size === 1 && (this.#t.addObserver(this), j(this.#t, this.options) ? this.#l() : this.updateResult(), this.#g());
  }
  onUnsubscribe() {
    this.hasListeners() || this.destroy();
  }
  shouldFetchOnReconnect() {
    return T(
      this.#t,
      this.options,
      this.options.refetchOnReconnect
    );
  }
  shouldFetchOnWindowFocus() {
    return T(
      this.#t,
      this.options,
      this.options.refetchOnWindowFocus
    );
  }
  destroy() {
    this.listeners = /* @__PURE__ */ new Set(), this.#O(), this.#y(), this.#t.removeObserver(this);
  }
  setOptions(t) {
    const e = this.options, s = this.#t;
    if (this.options = this.#s.defaultQueryOptions(t), this.options.enabled !== void 0 && typeof this.options.enabled != "boolean" && typeof this.options.enabled != "function" && typeof o(this.options.enabled, this.#t) != "boolean")
      throw new Error(
        "Expected enabled to be a boolean or a callback that returns a boolean"
      );
    this.#S(), this.#t.setOptions(this.options), e._defaulted && !C(this.options, e) && this.#s.getQueryCache().notify({
      type: "observerOptionsUpdated",
      query: this.#t,
      observer: this
    });
    const i = this.hasListeners();
    i && W(
      this.#t,
      s,
      this.options,
      e
    ) && this.#l(), this.updateResult(), i && (this.#t !== s || o(this.options.enabled, this.#t) !== o(e.enabled, this.#t) || d(this.options.staleTime, this.#t) !== d(e.staleTime, this.#t)) && this.#v();
    const a = this.#m();
    i && (this.#t !== s || o(this.options.enabled, this.#t) !== o(e.enabled, this.#t) || a !== this.#n) && this.#R(a);
  }
  getOptimisticResult(t) {
    const e = this.#s.getQueryCache().build(this.#s, t), s = this.createResult(e, t);
    return J(this, s) && (this.#e = s, this.#c = this.options, this.#a = this.#t.state), s;
  }
  getCurrentResult() {
    return this.#e;
  }
  trackResult(t, e) {
    return new Proxy(t, {
      get: (s, i) => (this.trackProp(i), e?.(i), i === "promise" && (this.trackProp("data"), !this.options.experimental_prefetchInRender && this.#i.status === "pending" && this.#i.reject(
        new Error(
          "experimental_prefetchInRender feature flag is not enabled"
        )
      )), Reflect.get(s, i))
    });
  }
  trackProp(t) {
    this.#f.add(t);
  }
  getCurrentQuery() {
    return this.#t;
  }
  refetch({ ...t } = {}) {
    return this.fetch({
      ...t
    });
  }
  fetchOptimistic(t) {
    const e = this.#s.defaultQueryOptions(t), s = this.#s.getQueryCache().build(this.#s, e);
    return s.fetch().then(() => this.createResult(s, e));
  }
  fetch(t) {
    return this.#l({
      ...t,
      cancelRefetch: t.cancelRefetch ?? !0
    }).then(() => (this.updateResult(), this.#e));
  }
  #l(t) {
    this.#S();
    let e = this.#t.fetch(
      this.options,
      t
    );
    return t?.throwOnError || (e = e.catch(K)), e;
  }
  #v() {
    this.#O();
    const t = d(
      this.options.staleTime,
      this.#t
    );
    if (L.isServer() || this.#e.isStale || !A(t))
      return;
    const s = N(this.#e.dataUpdatedAt, t) + 1;
    this.#h = R.setTimeout(() => {
      this.#e.isStale || this.updateResult();
    }, s);
  }
  #m() {
    return (typeof this.options.refetchInterval == "function" ? this.options.refetchInterval(this.#t) : this.options.refetchInterval) ?? !1;
  }
  #R(t) {
    this.#y(), this.#n = t, !(L.isServer() || o(this.options.enabled, this.#t) === !1 || !A(this.#n) || this.#n === 0) && (this.#o = R.setInterval(() => {
      (this.options.refetchIntervalInBackground || z.isFocused()) && this.#l();
    }, this.#n));
  }
  #g() {
    this.#v(), this.#R(this.#m());
  }
  #O() {
    this.#h && (R.clearTimeout(this.#h), this.#h = void 0);
  }
  #y() {
    this.#o && (R.clearInterval(this.#o), this.#o = void 0);
  }
  createResult(t, e) {
    const s = this.#t, i = this.options, a = this.#e, f = this.#a, g = this.#c, O = t !== s ? t.state : this.#p, { state: w } = t;
    let r = { ...w }, D = !1, n;
    if (e._optimisticResults) {
      const h = this.hasListeners(), u = !h && j(t, e), b = h && W(t, s, e, i);
      (u || b) && (r = {
        ...r,
        ...H(w.data, t.options)
      }), e._optimisticResults === "isRestoring" && (r.fetchStatus = "idle");
    }
    let { error: F, errorUpdatedAt: E, status: c } = r;
    n = r.data;
    let U = !1;
    if (e.placeholderData !== void 0 && n === void 0 && c === "pending") {
      let h;
      a?.isPlaceholderData && e.placeholderData === g?.placeholderData ? (h = a.data, U = !0) : h = typeof e.placeholderData == "function" ? e.placeholderData(
        this.#d?.state.data,
        this.#d
      ) : e.placeholderData, h !== void 0 && (c = "success", n = _(
        a?.data,
        h,
        e
      ), D = !0);
    }
    if (e.select && n !== void 0 && !U)
      if (a && n === f?.data && e.select === this.#b)
        n = this.#u;
      else
        try {
          this.#b = e.select, n = e.select(n), n = _(a?.data, n, e), this.#u = n, this.#r = null;
        } catch (h) {
          this.#r = h;
        }
    this.#r && (F = this.#r, n = this.#u, E = Date.now(), c = "error");
    const y = r.fetchStatus === "fetching", S = c === "pending", I = c === "error", P = S && y, M = n !== void 0, l = {
      status: c,
      fetchStatus: r.fetchStatus,
      isPending: S,
      isSuccess: c === "success",
      isError: I,
      isInitialLoading: P,
      isLoading: P,
      data: n,
      dataUpdatedAt: r.dataUpdatedAt,
      error: F,
      errorUpdatedAt: E,
      failureCount: r.fetchFailureCount,
      failureReason: r.fetchFailureReason,
      errorUpdateCount: r.errorUpdateCount,
      isFetched: t.isFetched(),
      isFetchedAfterMount: r.dataUpdateCount > O.dataUpdateCount || r.errorUpdateCount > O.errorUpdateCount,
      isFetching: y,
      isRefetching: y && !S,
      isLoadingError: I && !M,
      isPaused: r.fetchStatus === "paused",
      isPlaceholderData: D,
      isRefetchError: I && M,
      isStale: Q(t, e),
      refetch: this.refetch,
      promise: this.#i,
      isEnabled: o(e.enabled, t) !== !1
    };
    if (this.options.experimental_prefetchInRender) {
      const h = l.data !== void 0, u = l.status === "error" && !h, b = (m) => {
        u ? m.reject(l.error) : h && m.resolve(l.data);
      }, x = () => {
        const m = this.#i = l.promise = k();
        b(m);
      }, v = this.#i;
      switch (v.status) {
        case "pending":
          t.queryHash === s.queryHash && b(v);
          break;
        case "fulfilled":
          (u || l.data !== v.value) && x();
          break;
        case "rejected":
          (!u || l.error !== v.reason) && x();
          break;
      }
    }
    return l;
  }
  updateResult() {
    const t = this.#e, e = this.createResult(this.#t, this.options);
    if (this.#a = this.#t.state, this.#c = this.options, this.#a.data !== void 0 && (this.#d = this.#t), C(e, t))
      return;
    this.#e = e;
    const s = () => {
      if (!t)
        return !0;
      const { notifyOnChangeProps: i } = this.options, a = typeof i == "function" ? i() : i;
      if (a === "all" || !a && !this.#f.size)
        return !0;
      const f = new Set(
        a ?? this.#f
      );
      return this.options.throwOnError && f.add("error"), Object.keys(this.#e).some((g) => {
        const p = g;
        return this.#e[p] !== t[p] && f.has(p);
      });
    };
    this.#I({ listeners: s() });
  }
  #S() {
    const t = this.#s.getQueryCache().build(this.#s, this.options);
    if (t === this.#t)
      return;
    const e = this.#t;
    this.#t = t, this.#p = t.state, this.hasListeners() && (e?.removeObserver(this), t.addObserver(this));
  }
  onQueryUpdate() {
    this.updateResult(), this.hasListeners() && this.#g();
  }
  #I(t) {
    B.batch(() => {
      t.listeners && this.listeners.forEach((e) => {
        e(this.#e);
      }), this.#s.getQueryCache().notify({
        query: this.#t,
        type: "observerResultsUpdated"
      });
    });
  }
};
function G(t, e) {
  return o(e.enabled, t) !== !1 && t.state.data === void 0 && !(t.state.status === "error" && e.retryOnMount === !1);
}
function j(t, e) {
  return G(t, e) || t.state.data !== void 0 && T(t, e, e.refetchOnMount);
}
function T(t, e, s) {
  if (o(e.enabled, t) !== !1 && d(e.staleTime, t) !== "static") {
    const i = typeof s == "function" ? s(t) : s;
    return i === "always" || i !== !1 && Q(t, e);
  }
  return !1;
}
function W(t, e, s, i) {
  return (t !== e || o(i.enabled, t) === !1) && (!s.suspense || t.state.status !== "error") && Q(t, s);
}
function Q(t, e) {
  return o(e.enabled, t) !== !1 && t.isStaleByTime(d(e.staleTime, t));
}
function J(t, e) {
  return !C(t.getCurrentResult(), e);
}
export {
  rt as QueryObserver
};
