/**
 * Types for hooks
 * @author Xavier Guimard <xguimard@linagora.com>
 */
import type { SearchOptions, SearchResult } from 'ldapts';
import type { Request, Response } from 'express';

import type {
  ModifyRequest,
  AttributesList,
  AttributeValue,
} from './lib/ldapActions';
import type { ChangesToNotify } from './plugins/ldap/onChange';
import * as utils from './lib/utils';

export type MaybePromise<T> = Promise<T> | T;
export type ChainedHook<T> = (arg: T) => MaybePromise<T>;
export type VoidHook<T extends unknown[]> = (...args: T) => MaybePromise<void>;
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
export type OtherHook = Function;
export { utils };

/**
 * All available hooks
 */

export interface Hooks {
  /**
   * Libraries
   */

  /* LDAP */

  // search
  ldapsearchopts?: ChainedHook<SearchOptions>;
  ldapsearchrequest?: ChainedHook<[string, SearchOptions, Request?]>;
  ldapsearchresult?: ChainedHook<SearchResult>;
  // add
  ldapaddrequest?: ChainedHook<[string, AttributesList, Request?]>;
  ldapadddone?: (args: [string, AttributesList]) => MaybePromise<void>;
  // modify
  ldapmodifyrequest?: ChainedHook<[string, ModifyRequest, number, Request?]>;
  ldapmodifydone?: (
    args: [string, ModifyRequest, number]
  ) => MaybePromise<void>;
  // delete
  ldapdeleterequest?: ChainedHook<[string | string[], Request?]>;
  ldapdeletedone?: (dn: string | string[]) => MaybePromise<void>;
  // rename
  ldaprenamerequest?: ChainedHook<[string, string, Request?]>;
  ldaprenamedone?: (args: [string, string]) => MaybePromise<void>;

  /**
   * Plugins
   */

  /** Demo plugin */
  hello?: () => string;

  /** LdapGroups plugin */
  ldapgroupvalidatemembers?: ChainedHook<[string, string[]]>;
  ldapgroupadd?: ChainedHook<[string, AttributesList]>;
  ldapgroupadddone?: (args: [string, AttributesList]) => MaybePromise<void>;

  // the number given as 3rd argument is a uniq operation number
  // It can be used to save state before modify and launch the
  // real hook after change but with previous value
  ldapgroupmodify?: ChainedHook<[string, ModifyRequest, number]>;
  ldapgroupmodifydone?: (
    args: [string, ModifyRequest, number]
  ) => MaybePromise<void>;

  ldapgroupdelete?: ChainedHook<string>;
  ldapgroupdeletedone?: (dn: string) => MaybePromise<void>;
  ldapgroupaddmember?: ChainedHook<[string, string[]]>;
  ldapgroupdeletemember?: ChainedHook<[string, string[]]>;
  // this hook is for low-level ldap listGroups method
  _ldapgrouplist?: ChainedHook<AsyncGenerator<SearchResult>>;

  /** "onLdapChange" */
  onLdapChange?: (dn: string, changes: ChangesToNotify) => MaybePromise<void>;
  onLdapMailChange?: (
    dn: string,
    oldMail: AttributeValue | null,
    newMail: AttributeValue | null
  ) => MaybePromise<void>;
  onLdapAliasChange?: (
    dn: string,
    mail: string,
    oldAliases: string[],
    newAliases: string[]
  ) => MaybePromise<void>;
  onLdapQuotaChange?: (
    dn: string,
    mail: string,
    oldQuota: number,
    newQuota: number
  ) => MaybePromise<void>;
  onLdapForwardChange?: (
    dn: string,
    mail: string,
    oldForwards: string[],
    newForwards: string[]
  ) => MaybePromise<void>;
  onLdapDisplayNameChange?: (
    dn: string,
    oldDisplayName: string | null,
    newDisplayName: string | null
  ) => MaybePromise<void>;
  onLdapDriveQuotaChange?: (
    dn: string,
    oldDriveQuota: number | null,
    newDriveQuota: number | null
  ) => MaybePromise<void>;

  /** externalUsersInGroup */
  externaluserentry?: ChainedHook<[string, AttributesList]>;
  externaluseradded?: (dn: string, mail: string) => MaybePromise<void>;

  /**
   * Generic flat resource move hooks
   * Pattern: {hookPrefix}move
   * Examples: ldapusermove, ldappositionmove, etc.
   *
   * move: before moving - can modify target or cancel
   * Note: After move, onLdapChange is triggered automatically by modifyEntry()
   */
  // Note: These are defined dynamically via the index signature below
  // but documented here for reference:
  // - ldapusermove?: ChainedHook<[string, string, Request?]>  // [dn, targetOrgDn, req]

  // External hooks (allows dynamic hook names like ldapusermove, ldapgroupmove, etc.)
  [K: string]:
    | ChainedHook<unknown>
    | VoidHook<unknown[]>
    | OtherHook
    | undefined;

  /** Common authentication hooks */
  beforeAuth?: ChainedHook<[Request, Response]>;
  afterAuth?: ChainedHook<[Request, Response]>;

  /** Organization hooks */
  getOrganisationTop?: ChainedHook<
    [Request | undefined, AttributesList | null]
  >;

  /**
   * SCIM plugin hooks (typed dynamically via the index signature above)
   *
   * - scimusercreate:      ChainedHook<[ScimUser, Request?]>     — pre-create, can mutate
   * - scimusercreatedone:  VoidHook<[ScimUser]>                   — post-create
   * - scimuserupdate:      ChainedHook<[string, ScimUser, Request?]>
   * - scimuserupdatedone:  VoidHook<[string, ScimUser]>
   * - scimuserdelete:      ChainedHook<[string, Request?]>
   * - scimuserdeletedone:  VoidHook<[string]>
   * - scimgroupcreate:     ChainedHook<[ScimGroup, Request?]>
   * - scimgroupcreatedone: VoidHook<[ScimGroup]>
   * - scimgroupupdate:     ChainedHook<[string, ScimGroup, Request?]>
   * - scimgroupupdatedone: VoidHook<[string, ScimGroup]>
   * - scimgroupdelete:     ChainedHook<[string, Request?]>
   * - scimgroupdeletedone: VoidHook<[string]>
   * - scimbulkdone:        VoidHook<[BulkResponse]>
   */
}
