import { AxiosInstance } from 'axios';
import debug from 'debug';

type CustomerID = number
type AccessIdentificationID = number

interface SearchOptions {
  facility?: unitID
  /**
   * searchInAddress
   * @default false
   */
  searchInAddress?: boolean
  /**
   * searchInBankAccount
   * @default false
   */
  searchInBankAccount?: boolean
  /**
   * searchInCardNumber
   * @default false
   */
  searchInCardNumber?: boolean
  /**
   * searchInCustomerNumber
   * @default true
   */
  searchInCustomerNumber?: boolean
  /**
   * searchInLockerKey
   * @default false
   */
  searchInLockerKey?: boolean
  /**
   * searchInName
   * @default true
   */
  searchInName?: boolean
  /**
   * searchInPurchasedContingentCode
   * @default false
   */
  searchInPurchasedContingentCode?: boolean
  /**
   * showAllFacilities
   * @default true
   */
  showAllFacilities?: boolean
  /**
   * showCheckedIn
   * @default false
   */
  showCheckedIn?: boolean
  /**
   * showOnlyMembers
   * @default false
   */
  showOnlyMembers?: boolean
}

type customer_d$1_AccessIdentificationID = AccessIdentificationID;
type customer_d$1_CustomerID = CustomerID;
type customer_d$1_SearchOptions = SearchOptions;
declare namespace customer_d$1 {
  export {
    customer_d$1_AccessIdentificationID as AccessIdentificationID,
    customer_d$1_CustomerID as CustomerID,
    customer_d$1_SearchOptions as SearchOptions,
  };
}

interface ListOptions {
  organizationUnitId?: unitID
  offset?: number
  /** magiclines webclient sets this to  */
  maxResults?: number
  search?: string
  filter?: string
  sortedby?: "checkinTime" | "checkinDuration" | "lastname" | "firstname" | "lockerKey"
  /** list checkouts instead */
  checkouts?: boolean
  direction?: "DESCENDING" | "ASCENDING"
}

interface CheckinOptions {
  /** not sure what this is for */
  customerCardNumber?: null | unknown
  /** not sure what this is for */
  customerUUID?: string
  /** customerID */
  fkCustomer: number
  /** not sure what this is for */
  fkDevice?: null | unknown
  /** organizationUnitId */
  fkOrganizationUnit?: unitID
  lockerKey?: number | string
  /** not sure what this is for */
  purchasedContingentCode?: null | unknown
  /** not sure what this is for */
  databaseId?: null | unknown
  /** not sure what this is for, was 0 for me */
  optlock?: number
  /** organizationUnitId */
  requiredOrganizationUnitId?: unitID
}

interface CheckoutOptions {
  optLockRemote?: number
}

interface LockerKeyOptions {
  /** not sure what this is for */
  databaseId?: null | unknown
  /** not sure what this is for */
  optlock?: 0 | number
}

type checkin_d$1_CheckinOptions = CheckinOptions;
type checkin_d$1_CheckoutOptions = CheckoutOptions;
type checkin_d$1_ListOptions = ListOptions;
type checkin_d$1_LockerKeyOptions = LockerKeyOptions;
declare namespace checkin_d$1 {
  export {
    checkin_d$1_CheckinOptions as CheckinOptions,
    checkin_d$1_CheckoutOptions as CheckoutOptions,
    checkin_d$1_ListOptions as ListOptions,
    checkin_d$1_LockerKeyOptions as LockerKeyOptions,
  };
}

interface ProductOptions {
  organizationUnitId?: unitID
  customerId?: number
}

type sales_d$1_ProductOptions = ProductOptions;
declare namespace sales_d$1 {
  export {
    sales_d$1_ProductOptions as ProductOptions,
  };
}

interface Config {
  /**
   * the gym's url prefix
   *
   * when you access your dashboard via https://example.web.magicline.com
   * your url prefix will be `example`
   */
  gym: string
  /**
   * login username, making a dedicated account for API access is recommended.
   */
  username?: string
  /**
   * login password, making a dedicated account for API access is recommended.
   */
  password?: string
  // unitID?: number
}

type index_d$1_Config = Config;
declare namespace index_d$1 {
  export {
    checkin_d$1 as Checkin,
    index_d$1_Config as Config,
    customer_d$1 as Customer,
    sales_d$1 as Sales,
  };
}

interface Benefit {
  name: string
  available: number | null
  total: number | null
  hasUsages: boolean
  colorHex: string
  unit: string
  inclusiveContingentId: null
  purchasedContingentId: number | null
  trialContingentId: null
  freeUsage: boolean
  benefitId: number
  expiryDate: null | string
  createdDate: null | string
  durationText: null
  flat: boolean
}

interface Base {
  databaseId: CustomerID
}

interface CheckinCondition {
  type: string
  messageKey: string
  args: string[]
}

interface SearchedCustomer extends Base {
  firstname: string
  lastname: string
  gender: number
  dateOfBirth: Date | null
  lastCheckIn: Date | null
  facilityName: string
  customerNumber: string
  cardNumber: null
  lockerKey: null | string
  customerStatus: number
  purchasedContingentCode: null
  purchasedContingentType: null
  imageUrl: null
  checkedIn: boolean
  street: null | string
  houseNumber: null | string
  zip: null | string
  city: null | string
}

interface AccessIdentification {
  databaseId: AccessIdentificationID
  optlock: number
  fkOrganizationUnit: number
  createdDate: Date
  type: string
  uid: string
  stompDestination: string
}

interface Contract {
  databaseId: number
  optlock: number
  rateId: number
  rateName: string
  publicRateName: string
  compunknownId: null
  currencyUnit: string
  startDate: string
  startDateOfUse: null | string
  startDateCurrentTerm: null | string
  preuseType: number
  endDate: string
  term: number
  extensionType: string
  termUnit: number
  termExtension: number
  termExtensionUnit: number
  extensionCancelationPeriod: null
  trialPeriod: null
  paymentChoice: null
  cancelationPeriod: number
  cancelationPeriodUnit: number
  nextCancelationDate: string
  active: boolean
  activeAgeBasedAdjustmentDto: null
  isCanceled: boolean | null
  isReversed: boolean
  imported: boolean
  hasBonusPeriods: boolean
  hasContractIdlePeriods: boolean
  contractOutlier: unknown[]
  currentPrice: number
  currentPriceWithDate: CurrentPriceWithDate
  nextPriceWithDate: null
  contractCancelation: ContractCancelation | null
  paymentDay: number | null
  contractPaymentFrequency: ContractPaymentFrequency
  moduleContracts: null
  flatFeeContracts: FlatFeeContract[] | null
  divergentAffiliate: null
  contractRestMaturityDto: null
  cancelationStrategy: string
  subsequentRateName: null
  subsequentRateDetailTerm: null
  subsequentRateDetailTermUnit: null
  subsequentRateDetailPaymentFrequencyTerm: null
  subsequentRateDetailPaymentFrequencyTermUnit: null
  subsequentRateDetailPaymentFrequencyType: null
  subsequentRateDetailPaymentFrequencyPrice: null
  subsequentContractId: null
  subsequentContractHasSubsequentContract: boolean
  subsequentContractName: null
  signatureStatus: string
  campaignId: null
  salesSource: string
  notes: null
  bonusPeriods: unknown[]
  contractIdlePeriods: unknown[]
  vouchers: unknown[]
  paymentRunGroupSimpleDto: null
}

interface ContractCancelation {
  databaseId: number
  optlock: number
  fkCancelationReason: number
  cancelationReasonName: string
  cancelationReceiptDate: string
  cancelationDate: string
  extraordinaryCancelation: boolean
  createSystemMessage: boolean
  customerMessageId: null
  cancelationFee: null
  ignoreImportedChargesOnChargeCalendarCalculation: boolean
  cancelationByStudio: boolean
}

interface ContractPaymentFrequency {
  databaseId: number
  optlock: number
  type: string
  value: number | null
  unit: number | null
  price: number
  money: Money
  paidTimePeriodCalculationType: null | string
  firstBookingDelay: FirstBookingDelay | null
  recurring: null
  extensionValue: null
  extensionUnit: null
  extensionPrice: null
  dueDateOffset: null
  applyFirstEncashmentOnFirstRegularCharge: boolean
  monthDayWithPrices: unknown[]
  termWithPrices: unknown[]
  monthDays: unknown[]
  ageBasedAdjustmentDtos: unknown[]
  firstEncashment: string
  dynamicAdjustmentRules: unknown[]
}

interface FirstBookingDelay {
  term: number
  termUnit: number
}

interface Money {
  amount: number
  currencyCode: string
}

interface CurrentPriceWithDate {
  date: string
  price: number
}

interface FlatFeeContract {
  databaseId: number
  optlock: number
  currencyUnit: string
  startDate: string
  startDateOfUse: null
  endDate: string
  imported: boolean
  paymentDay: null
  contractCancelation: null
  contractPaymentFrequency: ContractPaymentFrequency
  contractOutlier: null
  contractRestMaturityDto: null
  retroactive: boolean
  termInformation: null
  paymentChoice: null
  paymentRunGroupSimpleDto: null
  flatFeeRateId: number
  flatFeeRateName: string
  flatFeeRatePublicName: string
  flatFeeRateDetailPaymentFrequencyId: number
  reversed: boolean
  canceled: boolean
}

interface DetailedBalance {
  databaseId: number
  optlock: null
  consumptionCreditBalance: number
  debtClaimBalance: number
  debtClaimBalanceWithoutLaterSale: number
  laterSaleBalance: number
  paymentBalance: number
  transferBalance: number
  totalWithoutConsumptionCredit: number
}

type customer_d_AccessIdentification = AccessIdentification;
type customer_d_Base = Base;
type customer_d_Benefit = Benefit;
type customer_d_CheckinCondition = CheckinCondition;
type customer_d_Contract = Contract;
type customer_d_ContractCancelation = ContractCancelation;
type customer_d_ContractPaymentFrequency = ContractPaymentFrequency;
type customer_d_CurrentPriceWithDate = CurrentPriceWithDate;
type customer_d_DetailedBalance = DetailedBalance;
type customer_d_FirstBookingDelay = FirstBookingDelay;
type customer_d_FlatFeeContract = FlatFeeContract;
type customer_d_Money = Money;
type customer_d_SearchedCustomer = SearchedCustomer;
declare namespace customer_d {
  export {
    customer_d_AccessIdentification as AccessIdentification,
    customer_d_Base as Base,
    customer_d_Benefit as Benefit,
    customer_d_CheckinCondition as CheckinCondition,
    customer_d_Contract as Contract,
    customer_d_ContractCancelation as ContractCancelation,
    customer_d_ContractPaymentFrequency as ContractPaymentFrequency,
    customer_d_CurrentPriceWithDate as CurrentPriceWithDate,
    customer_d_DetailedBalance as DetailedBalance,
    customer_d_FirstBookingDelay as FirstBookingDelay,
    customer_d_FlatFeeContract as FlatFeeContract,
    customer_d_Money as Money,
    customer_d_SearchedCustomer as SearchedCustomer,
  };
}

interface LockerKeyResponse {
  checkinId: number
  lockerKey: string
}

interface CheckinResponse {
  /** this is the checkinID **not** the customerID, that is `fkCustomer` */
  databaseId: number
  optlock: number
  fkOrganizationUnit: number
  /** this is the customerID */
  fkCustomer: number
  fkEmployee: null
  fkDevice: number | null
  firstname: string
  lastname: string
  cardNumber: null
  customerNumber: string
  employeeNumber: null
  dateOfBirth: string
  gender: number
  imageUrl: null
  studioName: null
  lockerKey: null | string
  checkinTime: string
  checkoutTime: null | string
  stompDestination: string
}

interface CheckinList {
  checkins: Checkin$1[]
  summary: { [key: string]: number }
}

interface Checkin$1 {
  databaseId: number
  optlock: null
  customerId: number
  customerOrganizationUnitId: number
  customerFacilityName: string
  lockerKey: null | string
  checkinTime: string
  previousCheckinTime: null | string
  lastCheckoutTime: string
  firstname: string
  lastname: string
  customerNumber: string
  gender: number
  dateOfBirth: null | string
  imageUrl: ImageURL$1 | null
  customerCodes: CustomerCode[]
  contractNames: string[]
  moduleNames: string[]
  badges: Badge[]
  isAnonymized: boolean
}

interface Badge {
  badge: string
  label: string
}

interface CustomerCode {
  databaseId: number
  optlock: number
  facilityInfo: null
  name: string
  description: null | string
  colorHex: string
  active: boolean
}

interface ImageURL$1 {
  url: string
  rewrite: boolean
}

type checkin_d_Badge = Badge;
type checkin_d_CheckinList = CheckinList;
type checkin_d_CheckinResponse = CheckinResponse;
type checkin_d_CustomerCode = CustomerCode;
type checkin_d_LockerKeyResponse = LockerKeyResponse;
declare namespace checkin_d {
  export {
    checkin_d_Badge as Badge,
    Checkin$1 as Checkin,
    checkin_d_CheckinList as CheckinList,
    checkin_d_CheckinResponse as CheckinResponse,
    checkin_d_CustomerCode as CustomerCode,
    ImageURL$1 as ImageURL,
    checkin_d_LockerKeyResponse as LockerKeyResponse,
  };
}

interface ProductOverview {
  fkOrganizationUnit: null
  customer: null
  classOfGoodsList: ClassOfGoodsList[]
}

interface ClassOfGoodsList {
  databaseId: number
  name: string
  materialClassOfGoods: boolean
  voucher: boolean
  productList: ProductList[]
}

interface ProductList {
  databaseId: number | null
  name: string
  regularTaxRate: TaxRate
  toGoTaxRate: TaxRate | null
  productVariantList: ProductVariantList[]
}

interface ProductVariantList {
  databaseId: number
  name: string
  itemNumber: string
  currencyUnit: string
  price: number
  additionalInfo: null | string
  stock: number | null
  ean: null | string
}

interface TaxRate {
  name: string
  percent: number
}

type sales_d_ClassOfGoodsList = ClassOfGoodsList;
type sales_d_ProductList = ProductList;
type sales_d_ProductOverview = ProductOverview;
type sales_d_ProductVariantList = ProductVariantList;
type sales_d_TaxRate = TaxRate;
declare namespace sales_d {
  export {
    sales_d_ClassOfGoodsList as ClassOfGoodsList,
    sales_d_ProductList as ProductList,
    sales_d_ProductOverview as ProductOverview,
    sales_d_ProductVariantList as ProductVariantList,
    sales_d_TaxRate as TaxRate,
  };
}

type CallbackFunction<T> = (data: T) => void
type UnsubscribeFunction = () => void

interface CheckinEvent {
  type: string
  action: string
  timestamp: number
  tenantName: string
  organizationUnitId: number
  customerIds: unknown[]
  payload: Payload
}

interface Payload {
  databaseId: number
  optlock: number
  fkOrganizationUnit: number
  fkCustomer: number
  fkEmployee: null
  fkDevice: number
  firstname: string
  lastname: string
  cardNumber: null
  customerNumber: string
  employeeNumber: null
  dateOfBirth: string
  gender: number
  imageUrl: ImageURL | null
  studioName: null
  lockerKey: null | string
  checkinTime: string
  checkoutTime: null | string
  stompDestination: string
}

interface ImageURL {
  url: string
  rewrite: boolean
}

type socket_d_CallbackFunction<T> = CallbackFunction<T>;
type socket_d_CheckinEvent = CheckinEvent;
type socket_d_ImageURL = ImageURL;
type socket_d_Payload = Payload;
type socket_d_UnsubscribeFunction = UnsubscribeFunction;
declare namespace socket_d {
  export {
    socket_d_CallbackFunction as CallbackFunction,
    socket_d_CheckinEvent as CheckinEvent,
    socket_d_ImageURL as ImageURL,
    socket_d_Payload as Payload,
    socket_d_UnsubscribeFunction as UnsubscribeFunction,
  };
}

type CurrentLocale = string
type SupportedLocales = string[]
interface Success {
  success: "true"
}
interface GenericError {
  errorMessage: string
  errorCode: string
  args: unknown[]
  typedArgs: unknown[]
}

type ErrorOrSuccess = Partial<Success> & GenericError[]

interface Permitted {
  databaseId: null | number
  optlock: null | number
  name: string
  prefix: null | string
  type: number
  country: null | string
  colorHex: string
  currencyCode: string
  timeZone: string
  currency: {
    databaseId: null | number
    optlock: null | number
    unit: string
  }
  isTenantWithSingleStudio: boolean
  permitted: boolean
  listChildren: Permitted[]
  permissions: string[]
  isStudioLoginPermitted: boolean
}
type Notices = unknown[]
interface AccountInfo {
  firstname: string
  lastname: string
  fullName: string
  employeeId: number
  imageUrl: {
    url: string
    rewrite: boolean
  }
}
interface App {
  app: string
  status: string
  values: {
    id?: number
    category: string
    tier?: string
    payment_provider_id?: string
  }
}

type index_d_AccountInfo = AccountInfo;
type index_d_App = App;
type index_d_CurrentLocale = CurrentLocale;
type index_d_ErrorOrSuccess = ErrorOrSuccess;
type index_d_GenericError = GenericError;
type index_d_Notices = Notices;
type index_d_Permitted = Permitted;
type index_d_Success = Success;
type index_d_SupportedLocales = SupportedLocales;
declare namespace index_d {
  export {
    index_d_AccountInfo as AccountInfo,
    index_d_App as App,
    checkin_d as Checkin,
    index_d_CurrentLocale as CurrentLocale,
    customer_d as Customer,
    index_d_ErrorOrSuccess as ErrorOrSuccess,
    index_d_GenericError as GenericError,
    index_d_Notices as Notices,
    index_d_Permitted as Permitted,
    sales_d as Sales,
    socket_d as Socket,
    index_d_Success as Success,
    index_d_SupportedLocales as SupportedLocales,
  };
}

/**
 * the "organizationUnitId" used to identify different units for the same gym.
 * that's what i think what it is at least.
 *
 * if it's marked as optional it can be omitted for convenience, in which case it
 * falls back to the first listed unit, **but it *should* always be provided**.
 *
 * mine was 2 by default for some reason, you can find yours using `.permitted()`
 */
type unitID = number

declare class Util {
    private axios;
    private mgl;
    constructor(axios: AxiosInstance, mgl: Openmagicline);
    getDefaultUnitID(): Promise<unitID>;
    /**
     * check if the login token works.
     */
    testLogin(): Promise<boolean>;
}

declare class Locale {
    private axios;
    constructor(axios: AxiosInstance);
    currentLocale(): Promise<CurrentLocale>;
    supportedLocales(): Promise<SupportedLocales>;
}

declare class Organization {
    private axios;
    private mgl;
    constructor(axios: AxiosInstance, mgl: Openmagicline);
    permitted(): Promise<Permitted>;
    accountInfo(): Promise<AccountInfo>;
    apps(unitID?: unitID): Promise<App[]>;
}

declare class Customer {
    private axios;
    private mgl;
    constructor(axios: AxiosInstance, mgl: Openmagicline);
    defaultSearchOptions: Required<SearchOptions>;
    search(searchString: string, options?: SearchOptions): Promise<SearchedCustomer[]>;
    getCards(customerID: CustomerID): Promise<AccessIdentification[]>;
    /**
     * get contracts of a customer
     * @param customerId customer id
     * @param isActive get only active contracts (default: `true`)
     */
    contract: (customerId: CustomerID, isActive?: boolean) => Promise<Contract[]>;
    checkinConditions: (customerId: number, organizationUnitId?: number) => Promise<CheckinCondition[]>;
    benefits: (customerId: CustomerID, active?: boolean | "both") => Promise<Benefit[]>;
    detailedBalance: (customerId: CustomerID) => Promise<DetailedBalance>;
}

declare class Checkin {
    private axios;
    private mgl;
    constructor(axios: AxiosInstance, mgl: Openmagicline);
    defaultListParams: ListOptions;
    /**
     * list all checked-in customers
     * @param options filter, sort, etc.
     */
    list(options?: ListOptions): Promise<CheckinList>;
    private defaultCheckinParams;
    /**
     * check-in a customer
     */
    checkin(options: CheckinOptions): Promise<CheckinResponse>;
    /**
     * 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
     */
    checkout(checkinId: number, options?: CheckoutOptions): Promise<CheckinResponse>;
    private defaultLockerKeyParams;
    lockerKey(checkinId: number, lockerKey: number | string, options?: LockerKeyOptions): Promise<LockerKeyResponse>;
}

declare class Sales {
    private axios;
    private mgl;
    constructor(axios: AxiosInstance, mgl: Openmagicline);
    products(options?: ProductOptions): Promise<ProductOverview>;
}

declare class MagicSocket {
    private mgl;
    private client;
    private log;
    isActive: false | Promise<unknown>;
    private webSocketFactory;
    constructor(mgl: Openmagicline, unitID: unitID);
    subscriptions: Record<string, UnsubscribeFunction>;
    activate(): Promise<unknown>;
    unsubscribeAll(): Promise<void>;
    deactivate(): Promise<void>;
    private deactivateAutomatically;
    private subscribeFactory;
    onCheckin: (callback: CallbackFunction<CheckinEvent>) => Promise<UnsubscribeFunction>;
}

/** @deprecated todo: move to util */
declare const _log: debug.Debugger;

declare class Openmagicline {
    private config;
    protected log: debug.Debugger;
    protected axios: AxiosInstance;
    baseUrl: string;
    cookies?: string[];
    customer: Customer;
    locale: Locale;
    organization: Organization;
    /** everything related to the checkin process */
    checkin: Checkin;
    /** miscellaneous helpers and thingies */
    util: Util;
    /** everything related to retail sales (magicline calls this disposal in some places) */
    sales: Sales;
    /** reference to this.sales */
    disposal: Sales;
    /** event handler for magiclines websockets */
    socket: (unitID: unitID) => MagicSocket;
    constructor(config: Config, axios?: AxiosInstance);
    private _login;
    /**
     * 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: (cookies?: string[]) => Promise<Openmagicline>;
}

export { index_d as Magicline, index_d$1 as OMGL, Openmagicline, _log, unitID };
