"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// src/index.ts
var src_exports = {};
__export(src_exports, {
  Openmagicline: () => Openmagicline,
  _log: () => _log
});
module.exports = __toCommonJS(src_exports);
var import_axios_auth_refresh = __toESM(require("axios-auth-refresh"));
var import_axios = __toESM(require("axios"));
var import_once = __toESM(require("lodash/once"));
var import_debug = __toESM(require("debug"));

// src/util.ts
var import_form_data = __toESM(require("form-data"));

// src/constants.ts
var DEFAULT_UNIT_ID = 1;

// src/util.ts
var Util = class {
  constructor(axios, mgl) {
    this.axios = axios;
    this.mgl = mgl;
  }
  async getDefaultUnitID() {
    const data = await this.mgl.organization.permitted();
    return data.listChildren[0].databaseId ?? DEFAULT_UNIT_ID;
  }
  /**
   * check if the login token works.
   */
  async testLogin() {
    try {
      await this.mgl.locale.currentLocale();
      return true;
    } catch {
      return false;
    }
  }
};
var headers = (mgl) => {
  const u = new URL(mgl.baseUrl);
  const returnValue = {
    authority: u.hostname,
    "sec-ch-ua": `"Google Chrome";v="107", "Chromium";v="107", "Not=A?Brand";v="24"`,
    accept: `application/json, text/javascript, */*; q=0.01`,
    "x-requested-with": `XMLHttpRequest`,
    "sec-ch-ua-mobile": `?0`,
    "user-agent": `Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36`,
    origin: u.href,
    "sec-fetch-site": `same-origin`,
    "sec-fetch-mode": `cors`,
    "sec-fetch-dest": `empty`,
    referer: u.href,
    "accept-language": `en-CA,en-US;q=0.9,en;q=0.8,de-DE;q=0.7,de;q=0.6,en-GB;q=0.5`
  };
  if (mgl.cookies)
    returnValue.cookie = mgl.cookies.join("");
  return returnValue;
};
var websocketHeaders = (mgl) => {
  const u = new URL(mgl.baseUrl);
  const returnValue = {
    Pragma: "no-cache",
    Origin: u.href,
    "Accept-Language": "en-CA,en-US;q=0.9,en;q=0.8,de-DE;q=0.7,de;q=0.6,en-GB;q=0.5",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
    "Cache-Control": "no-cache"
  };
  if (mgl.cookies)
    returnValue.cookie = mgl.cookies.join("");
  return returnValue;
};
var searchParameters = (data) => {
  const parameters = new URLSearchParams();
  for (const [key, value] of Object.entries(data))
    parameters.set(key, value);
  return parameters;
};

// src/locale.ts
var Locale = class {
  constructor(axios) {
    this.axios = axios;
  }
  async currentLocale() {
    const { data } = await this.axios.get("currentLocale");
    return data;
  }
  async supportedLocales() {
    const { data } = await this.axios.get("supportedLocales");
    return data;
  }
};

// src/organization.ts
var Organization = class {
  constructor(axios, mgl) {
    this.axios = axios;
    this.mgl = mgl;
  }
  async permitted() {
    const { data } = await this.axios("organizationunit/permitted");
    return data;
  }
  async accountInfo() {
    const { data } = await this.axios("me/info");
    return data;
  }
  async apps(unitID) {
    if (!unitID)
      unitID = await this.mgl.util.getDefaultUnitID();
    const { data } = await this.axios("app", {
      params: { organizationUnitId: unitID }
    });
    return data;
  }
};

// src/customer.ts
var Customer = class {
  constructor(axios, mgl) {
    this.axios = axios;
    this.mgl = mgl;
  }
  defaultSearchOptions = {
    facility: 0,
    searchInName: true,
    searchInCustomerNumber: true,
    searchInAddress: false,
    searchInBankAccount: false,
    searchInCardNumber: false,
    searchInLockerKey: false,
    searchInPurchasedContingentCode: false,
    showAllFacilities: true,
    showCheckedIn: false,
    showOnlyMembers: false
  };
  async search(searchString, options) {
    const { data } = await this.axios.post("customersearch", {
      ...this.defaultSearchOptions,
      ...options,
      searchString
    });
    return data;
  }
  async getCards(customerID) {
    const { data } = await this.axios(`customer/${customerID}/accessidentification`);
    return data;
  }
  /**
   * get contracts of a customer
   * @param customerId customer id
   * @param isActive get only active contracts (default: `true`)
   */
  contract = async (customerId, isActive = true) => {
    const { data } = await this.axios.get("contract", {
      params: { customerId, isActive }
    });
    return data;
  };
  checkinConditions = async (customerId, organizationUnitId) => {
    if (typeof organizationUnitId !== "number") {
      organizationUnitId = await this.mgl.util.getDefaultUnitID();
    }
    const { data } = await this.axios.get(`customer/${customerId}/conditions/checkin`, {
      params: { organizationUnitId }
    });
    return data;
  };
  benefits = async (customerId, active = "both") => {
    const returnList = [];
    if (active === true || active === "both") {
      const { data } = await this.axios.get("benefitaccount", {
        params: { customerId, active: true }
      });
      returnList.push(...data);
    }
    if (active === false || active === "both") {
      const { data } = await this.axios.get("benefitaccount", {
        params: { customerId, active: false }
      });
      returnList.push(...data);
    }
    return returnList;
  };
  detailedBalance = async (customerId) => {
    const { data } = await this.axios.get(`customer/${customerId}/balance/detailed`);
    return data;
  };
  // todo: implement card methods
  //? removed until more card methods are implemented
  // removeCard(
  //   customerID: Openmagicline.Customer.CustomerID,
  //   AccessIdentificationID: Openmagicline.Customer.AccessIdentificationID
  // ): Promise<Magicline.ErrorOrSuccess> {
  //   return this.got(`customer/${customerID}/accessidentification/${AccessIdentificationID}`, {
  //     method: "DELETE",
  //     searchParams: {
  //       optLockRemote: 0,
  //     },
  //   }).json()
  // }
};

// src/checkin.ts
var Checkin = class {
  constructor(axios, mgl) {
    this.axios = axios;
    this.mgl = mgl;
  }
  defaultListParams = {
    organizationUnitId: DEFAULT_UNIT_ID,
    checkouts: false,
    offset: 0,
    maxResults: 25,
    search: "",
    filter: "",
    sortedby: "checkinTime",
    direction: "DESCENDING"
  };
  /**
   * list all checked-in customers
   * @param options filter, sort, etc.
   */
  async list(options) {
    let organizationUnitId = options?.organizationUnitId;
    if (typeof organizationUnitId !== "number") {
      organizationUnitId = await this.mgl.util.getDefaultUnitID();
    }
    const { data } = await this.axios("checkin", {
      params: {
        ...this.defaultListParams,
        organizationUnitId,
        ...options
      }
    });
    return data;
  }
  defaultCheckinParams = {
    customerCardNumber: void 0,
    customerUUID: "",
    fkCustomer: 0,
    fkDevice: void 0,
    fkOrganizationUnit: DEFAULT_UNIT_ID,
    lockerKey: "",
    purchasedContingentCode: void 0,
    databaseId: void 0,
    optlock: 0,
    requiredOrganizationUnitId: DEFAULT_UNIT_ID
  };
  /**
   * check-in a customer
   */
  async checkin(options) {
    let unitID = options.requiredOrganizationUnitId ?? options.fkOrganizationUnit;
    if (typeof unitID !== "number") {
      unitID = await this.mgl.util.getDefaultUnitID();
    }
    const { data } = await this.axios.post("checkin", {
      ...this.defaultCheckinParams,
      fkOrganizationUnit: unitID,
      requiredOrganizationUnitId: unitID,
      ...options
    });
    return data;
  }
  /**
   * check-out a customer
   * @param checkinId the ID of the checkin, **not** the customer ID
   * @param options optional object containing optLockRemote, not sure what it does
   */
  async checkout(checkinId, options) {
    const { data } = await this.axios.delete(`checkin/${checkinId}`, {
      params: { ...options }
    });
    return data;
  }
  defaultLockerKeyParams = {
    databaseId: void 0,
    optlock: 0
  };
  async lockerKey(checkinId, lockerKey, options) {
    const { data } = await this.axios.put(`checkin/lockerkey/${checkinId}`, {
      ...this.defaultLockerKeyParams,
      checkinId,
      lockerKey,
      ...options
    });
    return data;
  }
};

// src/sales.ts
var Sales = class {
  constructor(axios, mgl) {
    this.axios = axios;
    this.mgl = mgl;
  }
  async products(options) {
    const organizationUnitId = options?.organizationUnitId ?? await this.mgl.util.getDefaultUnitID();
    const { data } = await this.axios("sales/productoverview", {
      params: {
        organizationUnitId,
        ...options
      }
    });
    return data;
  }
};

// src/socket.ts
var import_stompjs = require("@stomp/stompjs");
var import_ws = __toESM(require("ws"));
var MagicSocket = class {
  constructor(mgl, unitID) {
    this.mgl = mgl;
    this.client = new import_stompjs.Client({
      debug: this.log,
      heartbeatIncoming: 1e4,
      heartbeatOutgoing: 1e4,
      webSocketFactory: this.webSocketFactory(mgl.baseUrl, unitID),
      onWebSocketError: (
        /* istanbul ignore next */
        (error) => {
          console.log(error.target._req.res);
          throw error;
        }
      ),
      onStompError: (
        /* istanbul ignore next */
        (error) => {
          throw error;
        }
      )
    });
  }
  client;
  log = _log.extend("socket");
  isActive = false;
  webSocketFactory = (baseUrl, unitID) => () => {
    const socketURL = new URL(baseUrl);
    socketURL.protocol = "wss:";
    socketURL.pathname = "ws";
    socketURL.searchParams.set("organizationUnitId", unitID.toString());
    this.log(`connecting to ${socketURL.href}`);
    return new import_ws.default(socketURL.href, [], {
      headers: websocketHeaders(this.mgl)
    });
  };
  subscriptions = {};
  activate() {
    if (this.isActive)
      return this.isActive;
    this.isActive = new Promise((resolve) => {
      this.client.onConnect = resolve;
      this.client.activate();
    });
    return this.isActive;
  }
  unsubscribeAll() {
    for (const [key, unsubscribe] of Object.entries(this.subscriptions)) {
      unsubscribe();
      delete this.subscriptions[key];
    }
    return this.deactivate();
  }
  deactivate() {
    this.log("deactivating STOMP");
    this.isActive = false;
    return this.client.deactivate();
  }
  deactivateAutomatically() {
    if (Object.entries(this.subscriptions).length === 0)
      this.deactivate();
    else
      this.log("there are still active subscriptions, keeping stomp active");
  }
  /* eslint-disable @typescript-eslint/indent */
  subscribeFactory = (topic) => async (callback) => {
    await this.activate();
    this.client.subscribe(topic, ({ body }) => callback(JSON.parse(body)));
    this.log(`subscribed to "${topic}"`);
    const unsubscribe = () => {
      this.client.unsubscribe(topic);
      delete this.subscriptions[topic];
      this.deactivateAutomatically();
    };
    this.subscriptions[topic] = unsubscribe;
    return this.subscriptions[topic];
  };
  /* eslint-enable @typescript-eslint/indent */
  onCheckin = this.subscribeFactory("/user/topic/checkin");
};

// src/index.ts
var _log = (0, import_debug.default)("openmagicline");
var Openmagicline = class {
  // TODO: check version and warn if openmagicline is outdated
  constructor(config, axios) {
    this.config = config;
    this.log = _log.extend(config.gym);
    this.baseUrl = `https://${this.config.gym}.web.magicline.com`;
    const prefixUrl = `${this.baseUrl}/rest-api`;
    const httpAxiosLog = this.log.extend("http");
    if (axios)
      this.axios = axios;
    else {
      this.axios = import_axios.default.create({
        baseURL: prefixUrl,
        headers: headers(this)
      });
    }
    this.axios.interceptors.request.use((config2) => {
      if (this.cookies)
        config2.headers.cookie = this.cookies;
      return config2;
    });
    this.axios.interceptors.response.use((response) => {
      let log = `[${response.config.method}](${response.status}) `;
      log += response.config.url;
      if (response.status > 200)
        log += `
${response.data}`;
      httpAxiosLog(log);
      return response;
    });
    (0, import_axios_auth_refresh.default)(this.axios, () => {
      console.log("request failed, refreshing token");
      return this.login();
    });
    this.customer = new Customer(this.axios, this);
    this.locale = new Locale(this.axios);
    this.organization = new Organization(this.axios, this);
    this.checkin = new Checkin(this.axios, this);
    this.util = new Util(this.axios, this);
    this.sales = new Sales(this.axios, this);
    this.disposal = this.sales;
    this.socket = (unitID) => new MagicSocket(this, unitID);
  }
  log;
  axios;
  baseUrl;
  cookies;
  customer;
  locale;
  organization;
  /** everything related to the checkin process */
  checkin;
  /** miscellaneous helpers and thingies */
  util;
  /** everything related to retail sales (magicline calls this disposal in some places) */
  sales;
  /** reference to this.sales */
  disposal;
  /** event handler for magiclines websockets */
  socket;
  _login = async (cookies) => {
    if (cookies) {
      this.cookies = cookies;
      this.login = (0, import_once.default)(this._login);
      if (await this.util.testLogin()) {
        return this;
      } else {
        this.cookies = void 0;
        throw new Error("invalid token");
      }
    }
    try {
      const { username, password } = this.config;
      if (!username || !password)
        throw new Error("username and password need to be set when cookies aren't provided");
      const response = await this.axios.post(
        "login",
        searchParameters({ username, password, client: "webclient" }),
        // @ts-expect-error i am too lazy to fix these types ngl
        { baseURL: this.baseUrl, skipAuthRefresh: true }
      );
      this.login = (0, import_once.default)(this._login);
      this.cookies = response.headers["set-cookie"];
    } catch (error_) {
      this.cookies = void 0;
      const error = error_?.response?.data?.error_description ? new Error(error_.response.data.error_description) : error_;
      throw error;
    }
    return this;
  };
  /**
   * authenticate the Openmagicline instance using username/password from the instance config.
   *
   * if a token is passed, it will be validated and the request to `/login` will be skipped.
   * @param cookies existing cookies, available after login at `.cookies`
   * @returns instance for chaining
   * @throws when not authenticated
   */
  login = (0, import_once.default)(this._login);
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  Openmagicline,
  _log
});
