import { unreachable } from "../../../shared/src/asserts.js";
import { getErrorMessage } from "../../../shared/src/error.js";
import { MutationFailed, MutationRateLimited, Unauthorized, AuthInvalidated, ServerOverloaded, Rehome, Rebalance, TransformFailed, PushFailed, Internal as Internal$1, SchemaVersionNotSupported, VersionNotSupported, InvalidPush, InvalidMessage as InvalidMessage$1, InvalidConnectionRequestClientDeleted, InvalidConnectionRequestLastMutationID, InvalidConnectionRequestBaseCookie, InvalidConnectionRequest, ClientNotFound } from "../../../zero-protocol/src/error-kind-enum.js";
import { Client, Server, ZeroCache } from "../../../zero-protocol/src/error-origin-enum.js";
import { HTTP } from "../../../zero-protocol/src/error-reason-enum.js";
import { isProtocolError } from "../../../zero-protocol/src/error.js";
import { ClientClosed, NoSocketOrigin, Offline, Hidden, UserDisconnect, InvalidMessage, Internal, UnexpectedBaseCookie, PullTimeout, PingTimeout, ConnectTimeout, CleanClose, AbruptClose } from "./client-error-kind-enum.js";
import { NeedsAuth, Closed, Disconnected, Error as Error$1 } from "./connection-status-enum.js";
class ClientError extends Error {
  errorBody;
  constructor(errorBody, options) {
    super(errorBody.message, options);
    this.name = "ClientError";
    this.errorBody = { ...errorBody, origin: Client };
  }
  get kind() {
    return this.errorBody.kind;
  }
}
function isZeroError(ex) {
  return isClientError(ex) || isServerError(ex);
}
function isClientError(ex) {
  return ex instanceof ClientError && ex.errorBody.origin === Client;
}
function isServerError(ex) {
  return isProtocolError(ex) && (ex.errorBody.origin === Server || ex.errorBody.origin === ZeroCache);
}
function isAuthError(ex) {
  if (isServerError(ex)) {
    if (ex.kind === AuthInvalidated || ex.kind === Unauthorized) {
      return true;
    }
    if ((ex.errorBody.kind === PushFailed || ex.errorBody.kind === TransformFailed) && ex.errorBody.reason === HTTP && (ex.errorBody.status === 401 || ex.errorBody.status === 403)) {
      return true;
    }
  }
  return false;
}
function getBackoffParams(error) {
  if (isServerError(error)) {
    switch (error.errorBody.kind) {
      case Rebalance:
      case Rehome:
      case ServerOverloaded:
        return error.errorBody;
    }
  }
  return void 0;
}
const NO_STATUS_TRANSITION = "NO_STATUS_TRANSITION";
function getErrorConnectionTransition(ex) {
  if (isAuthError(ex)) {
    return {
      status: NeedsAuth,
      reason: ex
    };
  }
  if (isClientError(ex)) {
    switch (ex.kind) {
      // Connecting errors that should continue in the current state
      case AbruptClose:
      case CleanClose:
      case ConnectTimeout:
      case PingTimeout:
      case PullTimeout:
      case UnexpectedBaseCookie:
        return { status: NO_STATUS_TRANSITION, reason: ex };
      // Fatal errors that should transition to error state
      case Internal:
      case InvalidMessage:
      case UserDisconnect:
        return { status: Error$1, reason: ex };
      // Disconnected errors
      case Hidden:
      case Offline:
      case NoSocketOrigin:
        return {
          status: Disconnected,
          reason: ex
        };
      // Closed error (this should already result in a closed state)
      case ClientClosed:
        return { status: Closed, reason: ex };
      default:
        unreachable(ex.kind);
    }
  }
  if (isServerError(ex)) {
    switch (ex.kind) {
      // Errors that should transition to error state
      case ClientNotFound:
      case InvalidConnectionRequest:
      case InvalidConnectionRequestBaseCookie:
      case InvalidConnectionRequestLastMutationID:
      case InvalidConnectionRequestClientDeleted:
      case InvalidMessage$1:
      case InvalidPush:
      case VersionNotSupported:
      case SchemaVersionNotSupported:
      case Internal$1:
      // PushFailed and TransformFailed can be auth errors (401/403)
      // or other errors - handle non-auth cases here
      case PushFailed:
      case TransformFailed:
        return { status: Error$1, reason: ex };
      // Errors that should continue with backoff/retry
      case Rebalance:
      case Rehome:
      case ServerOverloaded:
        return { status: NO_STATUS_TRANSITION, reason: ex };
      // Auth errors are handled above by isAuthError check
      case AuthInvalidated:
      case Unauthorized:
        return {
          status: NeedsAuth,
          reason: ex
        };
      // Mutation-specific errors don't affect connection state
      case MutationRateLimited:
      case MutationFailed:
        return { status: NO_STATUS_TRANSITION, reason: ex };
      default:
        unreachable(ex.kind);
    }
  }
  return {
    status: Error$1,
    reason: new ClientError(
      {
        kind: Internal,
        message: "Unexpected internal error: " + getErrorMessage(ex)
      },
      { cause: ex }
    )
  };
}
export {
  ClientError,
  NO_STATUS_TRANSITION,
  getBackoffParams,
  getErrorConnectionTransition,
  isAuthError,
  isClientError,
  isServerError,
  isZeroError
};
//# sourceMappingURL=error.js.map
