import { notifyManager as a } from "./notifyManager.js";
import { Removable as u } from "./removable.js";
import { createRetryer as h } from "./retryer.js";
var f = class extends u {
  #o;
  #e;
  #t;
  #i;
  constructor(t) {
    super(), this.#o = t.client, this.mutationId = t.mutationId, this.#t = t.mutationCache, this.#e = [], this.state = t.state || c(), this.setOptions(t.options), this.scheduleGc();
  }
  setOptions(t) {
    this.options = t, this.updateGcTime(this.options.gcTime);
  }
  get meta() {
    return this.options.meta;
  }
  addObserver(t) {
    this.#e.includes(t) || (this.#e.push(t), this.clearGcTimeout(), this.#t.notify({
      type: "observerAdded",
      mutation: this,
      observer: t
    }));
  }
  removeObserver(t) {
    this.#e = this.#e.filter((o) => o !== t), this.scheduleGc(), this.#t.notify({
      type: "observerRemoved",
      mutation: this,
      observer: t
    });
  }
  optionalRemove() {
    this.#e.length || (this.state.status === "pending" ? this.scheduleGc() : this.#t.remove(this));
  }
  continue() {
    return this.#i?.continue() ?? // continuing a mutation assumes that variables are set, mutation must have been dehydrated before
    this.execute(this.state.variables);
  }
  async execute(t) {
    const o = () => {
      this.#s({ type: "continue" });
    }, e = {
      client: this.#o,
      meta: this.options.meta,
      mutationKey: this.options.mutationKey
    };
    this.#i = h({
      fn: () => this.options.mutationFn ? this.options.mutationFn(t, e) : Promise.reject(new Error("No mutationFn found")),
      onFail: (s, i) => {
        this.#s({ type: "failed", failureCount: s, error: i });
      },
      onPause: () => {
        this.#s({ type: "pause" });
      },
      onContinue: o,
      retry: this.options.retry ?? 0,
      retryDelay: this.options.retryDelay,
      networkMode: this.options.networkMode,
      canRun: () => this.#t.canRun(this)
    });
    const r = this.state.status === "pending", n = !this.#i.canStart();
    try {
      if (r)
        o();
      else {
        this.#s({ type: "pending", variables: t, isPaused: n }), this.#t.config.onMutate && await this.#t.config.onMutate(
          t,
          this,
          e
        );
        const i = await this.options.onMutate?.(
          t,
          e
        );
        i !== this.state.context && this.#s({
          type: "pending",
          context: i,
          variables: t,
          isPaused: n
        });
      }
      const s = await this.#i.start();
      return await this.#t.config.onSuccess?.(
        s,
        t,
        this.state.context,
        this,
        e
      ), await this.options.onSuccess?.(
        s,
        t,
        this.state.context,
        e
      ), await this.#t.config.onSettled?.(
        s,
        null,
        this.state.variables,
        this.state.context,
        this,
        e
      ), await this.options.onSettled?.(
        s,
        null,
        t,
        this.state.context,
        e
      ), this.#s({ type: "success", data: s }), s;
    } catch (s) {
      try {
        await this.#t.config.onError?.(
          s,
          t,
          this.state.context,
          this,
          e
        );
      } catch (i) {
        Promise.reject(i);
      }
      try {
        await this.options.onError?.(
          s,
          t,
          this.state.context,
          e
        );
      } catch (i) {
        Promise.reject(i);
      }
      try {
        await this.#t.config.onSettled?.(
          void 0,
          s,
          this.state.variables,
          this.state.context,
          this,
          e
        );
      } catch (i) {
        Promise.reject(i);
      }
      try {
        await this.options.onSettled?.(
          void 0,
          s,
          t,
          this.state.context,
          e
        );
      } catch (i) {
        Promise.reject(i);
      }
      throw this.#s({ type: "error", error: s }), s;
    } finally {
      this.#t.runNext(this);
    }
  }
  #s(t) {
    const o = (e) => {
      switch (t.type) {
        case "failed":
          return {
            ...e,
            failureCount: t.failureCount,
            failureReason: t.error
          };
        case "pause":
          return {
            ...e,
            isPaused: !0
          };
        case "continue":
          return {
            ...e,
            isPaused: !1
          };
        case "pending":
          return {
            ...e,
            context: t.context,
            data: void 0,
            failureCount: 0,
            failureReason: null,
            error: null,
            isPaused: t.isPaused,
            status: "pending",
            variables: t.variables,
            submittedAt: Date.now()
          };
        case "success":
          return {
            ...e,
            data: t.data,
            failureCount: 0,
            failureReason: null,
            error: null,
            status: "success",
            isPaused: !1
          };
        case "error":
          return {
            ...e,
            data: void 0,
            error: t.error,
            failureCount: e.failureCount + 1,
            failureReason: t.error,
            isPaused: !1,
            status: "error"
          };
      }
    };
    this.state = o(this.state), a.batch(() => {
      this.#e.forEach((e) => {
        e.onMutationUpdate(t);
      }), this.#t.notify({
        mutation: this,
        type: "updated",
        action: t
      });
    });
  }
};
function c() {
  return {
    context: void 0,
    data: void 0,
    error: null,
    failureCount: 0,
    failureReason: null,
    isPaused: !1,
    status: "idle",
    variables: void 0,
    submittedAt: 0
  };
}
export {
  f as Mutation,
  c as getDefaultState
};
