import { baseApi } from '../../base-api';
import type {
  ExamStateView,
  AppNotification,
  ApplicationDetail,
  ApplicationKind,
  ApplicationPayment,
  ApplicationStaff,
  Attachment,
  Department,
  DocumentRequirement,
  FormSchemaPalette,
  FormSectionConfig,
  InitiatePaymentResult,
  IssuedLicense,
  Inspection,
  IssuancePeriod,
  LicenseApplication,
  LicenseCategoryDefinition,
  LicenseStatus,
  LicenseType,
  LicenseTypeRequirements,
  OperatorType,
  AssignableOfficer,
  CertificateCategory,
  CompletionEffect,
  DocumentDecision,
  DocumentReview,
  ExportResult,
  LicenseTemplate,
  Paginated,
  PickupAppointment,
  PickupOffice,
  PickupSlot,
  QueueCounts,
  QueueFilter,
  Rank,
  RankCertificateCategory,
  RemarkTargetType,
  SavedQueueView,
  SchemaIssue,
  ServiceKind,
  StaffRoleRequirement,
  TemplateFieldPlacement,
  TemplateLogoPlacement,
  TemplatePageOptions,
  TemplateVariable,
  PersonalDocumentFilter,
  PersonalDocumentGroup,
  WorkflowProfile,
} from './licensing.types';

/**
 * Drops empty filters and flattens the status array.
 *
 * The grid keeps every facet in one object and serialises it to the URL, so
 * cleared facets arrive as undefined/[]; sending those verbatim would put
 * `status=` and `assignee=` on the wire and fail DTO validation.
 */
function serialiseQueueFilter(
  filter: QueueFilter | void,
): Record<string, unknown> {
  if (!filter) return {};
  const params: Record<string, unknown> = {};
  for (const [key, value] of Object.entries(filter)) {
    if (value === undefined || value === null || value === '') continue;
    if (Array.isArray(value)) {
      if (value.length === 0) continue;
      params[key] = value.join(',');
      continue;
    }
    params[key] = value;
  }
  return params;
}

/** Sends only the facets that are set; `search=` would match nothing. */
function dropEmpty(filter: object): Record<string, unknown> {
  const params: Record<string, unknown> = {};
  for (const [key, value] of Object.entries(filter)) {
    if (value === undefined || value === null || value === '' || value === false) continue;
    params[key] = value;
  }
  return params;
}

const TAGS = [
  'LicenseType',
  'OperatorType',
  'LicenseApplication',
  'ApplicationQueue',
  'Attachment',
  'Notification',
  'Inspection',
  'License',
  'SavedView',
  'LicenseTemplate',
  'DocumentRequirement',
  'PickupOffice',
  'PickupAppointment',
  'Department',
  'Rank',
  'StaffRoleRequirement',
  // Owned by the personal-document slice; named here so declaring a mode of
  // operation can invalidate the vault, whose slots depend on it.
  'PersonalDocument',
] as const;

const listTag = (type: (typeof TAGS)[number]) => ({ type, id: 'LIST' }) as const;
const itemTag = (type: (typeof TAGS)[number], id: string) => ({ type, id }) as const;

/**
 * Typed licensing endpoints, shared by the portal and the backoffice.
 *
 * Each resource gets its own cache tag so a transition invalidates the one
 * application and its queue, rather than the whole store.
 */
export const licensingApi = baseApi
  .enhanceEndpoints({ addTagTypes: TAGS })
  .injectEndpoints({
    endpoints: (builder) => ({
      // ------------------------------------------------------------- config
      /** Active licence types an applicant can choose from. */
      getLicenseTypes: builder.query<Paginated<LicenseType>, void>({
        query: () => ({ url: '/license-types' }),
        providesTags: () => [listTag('LicenseType')],
      }),

      /**
       * The signed-in applicant's declared modes of operation.
       *
       * Separate from the licence-type catalogue on purpose: the catalogue is
       * the same for everyone and heavily cached, while this is per-user and
       * changes the moment they edit their profile.
       */
      getMyOperatorTypes: builder.query<{ items: OperatorType[] }, void>({
        query: () => ({ url: '/profiles/me/operations' }),
        providesTags: () => [listTag('OperatorType')],
      }),

      /** Replaces the set — see the Operations tab in the portal profile. */
      updateMyOperatorTypes: builder.mutation<
        { items: OperatorType[] },
        { licenseTypeIds: string[] }
      >({
        query: (body) => ({
          url: '/profiles/me/operations',
          method: 'PUT',
          body,
        }),
        // The catalogue is filtered by this, so it has to refetch too — and so
        // is the personal document vault, which asks for the documents the
        // declared modes of operation need.
        invalidatesTags: (_r, error) =>
          error
            ? []
            : [
                listTag('OperatorType'),
                listTag('LicenseType'),
                listTag('PersonalDocument'),
              ],
      }),

      /**
       * Category catalogue used to group the licence cards. Static on the
       * server, so it is cached under the licence-type list tag.
       */
      getLicenseCategories: builder.query<
        Paginated<LicenseCategoryDefinition>,
        void
      >({
        query: () => ({ url: '/license-categories' }),
        providesTags: () => [listTag('LicenseType')],
      }),

      /**
       * Fee configuration, for licensing administrators.
       *
       * `null` is a meaningful value for both amounts and must survive the
       * round trip: null on the new-application fee means the licence is free,
       * null on the renewal fee means it is charged at the new-application
       * rate. Sending `undefined` instead would leave the column untouched.
       */
      updateLicenseFees: builder.mutation<
        LicenseType,
        {
          id: string;
          feeNewApplication?: number | null;
          feeRenewal?: number | null;
          feeCurrency?: string;
          // The examined-certificate stages. Unlike the two above, these are
          // read live off the licence type rather than snapshotted, so the
          // server refuses to clear one a candidate is currently waiting on.
          feeEligibility?: number | null;
          feeExamination?: number | null;
          feeCertificate?: number | null;
        }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-types/${id}/fees`,
          method: 'PATCH',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [listTag('LicenseType'), itemTag('LicenseType', id)],
      }),

      /**
       * How a licence type behaves: its workflow, eligibility gates, renewal
       * policy and applicant rules — everything that used to be settable only
       * by editing a seed file.
       *
       * Three of these (`workflowProfile`, `completionEffect`,
       * `requiresExamination`) decide the course an application runs, and are
       * read live rather than snapshotted. The server answers 409
       * `license_type_in_use` when changing one would strand applications that
       * have not yet had their approval decision.
       */
      updateLicenseBehavior: builder.mutation<
        LicenseType,
        {
          id: string;
          workflowProfile?: WorkflowProfile;
          serviceKind?: ServiceKind;
          completionEffect?: CompletionEffect | null;
          certificateCategory?: CertificateCategory | null;
          requiresExamination?: boolean;
          inspectionRequired?: boolean;
          issuesCertificate?: boolean;
          renewalEnabled?: boolean;
          requiresSeafarerRegistration?: boolean;
          requiresValidMedical?: boolean;
          minSeaTimeDays?: number | null;
          validityMonths?: number;
          validityDays?: number | null;
          capitalThreshold?: number | null;
          renewalWindowDays?: number;
          expiryReminderDays?: number[];
          requiresOperatorMode?: boolean;
          allowMultipleOpenDrafts?: boolean;
          requiresIssuanceScheduling?: boolean;
          uniqueFormKeyPath?: string | null;
          slaHours?: number | null;
        }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-types/${id}/behavior`,
          method: 'PATCH',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [listTag('LicenseType'), itemTag('LicenseType', id)],
      }),

      /** Validity is edited beside the certificate design, not with the fees. */
      updateLicenseValidity: builder.mutation<
        LicenseType,
        { id: string; validityMonths: number }
      >({
        query: ({ id, validityMonths }) => ({
          url: `/license-types/${id}/validity`,
          method: 'PATCH',
          body: { validityMonths },
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseType', id), listTag('LicenseType')],
      }),

      getLicenseTypeRequirements: builder.query<
        LicenseTypeRequirements,
        { idOrKey: string; kind?: ApplicationKind }
      >({
        query: ({ idOrKey, kind = 'NEW' }) => ({
          url: `/license-types/requirements/${idOrKey}`,
          params: { kind },
        }),
        providesTags: (_r, _e, arg) => [itemTag('LicenseType', arg.idOrKey)],
      }),

      // ------------------------------------------------ form-schema builder
      /** Replaces a licence type's form schema. Server re-validates on save. */
      updateFormSchema: builder.mutation<
        LicenseType,
        { id: string; formSchema: { sections: FormSectionConfig[] } }
      >({
        query: ({ id, formSchema }) => ({
          url: `/license-types/${id}/form-schema`,
          method: 'PUT',
          body: { formSchema },
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseType', id), listTag('LicenseType')],
      }),

      /** Dry-run lint, for inline feedback while the schema is being edited. */
      validateFormSchema: builder.mutation<
        { valid: boolean; issues: SchemaIssue[] },
        { formSchema: { sections: FormSectionConfig[] }; licenseTypeId?: string }
      >({
        query: (body) => ({
          url: '/license-types/form-schema/validate',
          method: 'POST',
          body,
        }),
      }),

      /** Field types, condition operators and prefill sources the builder may offer. */
      getFormSchemaPalette: builder.query<FormSchemaPalette, void>({
        query: () => ({ url: '/license-types/form-schema/palette' }),
      }),

      // ------------------------------------------------- document requirements
      /**
       * Every document requirement, for the admin editor to filter by licence
       * type client-side. The collection-query `q` filter syntax (`w=column:op:
       * value`) has no typed builder on this side, and the table is small
       * configuration data with no pagination need — see `licenseTypeId` usage
       * at the call site.
       */
      getDocumentRequirements: builder.query<Paginated<DocumentRequirement>, void>({
        query: () => ({ url: '/document-requirements' }),
        providesTags: () => [listTag('DocumentRequirement')],
      }),

      /**
       * Personal document slots, grouped by key and paged by the server.
       *
       * Its own endpoint rather than filtering `getDocumentRequirements` in the
       * browser: one document can be configured against several licence types,
       * so a page of rows would split a document in half and misreport its
       * scope. The server groups first, then pages.
       */
      getPersonalDocuments: builder.query<
        Paginated<PersonalDocumentGroup>,
        PersonalDocumentFilter | void
      >({
        query: (filter) => ({
          url: '/document-requirements/personal',
          params: dropEmpty(filter ?? {}),
        }),
        providesTags: () => [listTag('DocumentRequirement')],
      }),

      createDocumentRequirement: builder.mutation<
        DocumentRequirement,
        // No `licenseTypeId` means a personal document, required for every
        // licence and served from the applicant's own vault.
        Partial<DocumentRequirement> & { key: string; name: DocumentRequirement['name'] }
      >({
        query: (body) => ({ url: '/document-requirements', method: 'POST', body }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('DocumentRequirement')]),
      }),

      updateDocumentRequirement: builder.mutation<
        DocumentRequirement,
        { id: string } & Partial<DocumentRequirement>
      >({
        query: ({ id, ...body }) => ({
          url: `/document-requirements/${id}`,
          method: 'PUT',
          body,
        }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('DocumentRequirement')]),
      }),

      deleteDocumentRequirement: builder.mutation<unknown, string>({
        query: (id) => ({ url: `/document-requirements/${id}`, method: 'DELETE' }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('DocumentRequirement')]),
      }),

      // ----------------------------------------------- staff role requirements
      /**
       * Every staff role requirement, filtered by licence type at the call
       * site — same reasoning as `getDocumentRequirements`: small
       * configuration data with no pagination need.
       */
      getStaffRoleRequirements: builder.query<Paginated<StaffRoleRequirement>, void>({
        query: () => ({ url: '/staff-role-requirements' }),
        providesTags: () => [listTag('StaffRoleRequirement')],
      }),

      createStaffRoleRequirement: builder.mutation<
        StaffRoleRequirement,
        Partial<StaffRoleRequirement> & { licenseTypeId: string; roleKey: string; name: StaffRoleRequirement['name'] }
      >({
        query: (body) => ({ url: '/staff-role-requirements', method: 'POST', body }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('StaffRoleRequirement')]),
      }),

      updateStaffRoleRequirement: builder.mutation<
        StaffRoleRequirement,
        { id: string } & Partial<StaffRoleRequirement>
      >({
        query: ({ id, ...body }) => ({
          url: `/staff-role-requirements/${id}`,
          method: 'PUT',
          body,
        }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('StaffRoleRequirement')]),
      }),

      deleteStaffRoleRequirement: builder.mutation<unknown, string>({
        query: (id) => ({ url: `/staff-role-requirements/${id}`, method: 'DELETE' }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('StaffRoleRequirement')]),
      }),

      // --------------------------------------------------- departments & ranks
      /** Every department, for the admin editor. */
      getDepartments: builder.query<Paginated<Department>, void>({
        query: () => ({ url: '/departments' }),
        providesTags: () => [listTag('Department')],
      }),

      /** Active departments only — the applicant-facing picker. */
      getActiveDepartments: builder.query<Department[], void>({
        query: () => ({ url: '/departments/active/list' }),
        providesTags: () => [listTag('Department')],
      }),

      createDepartment: builder.mutation<
        Department,
        Partial<Department> & { code: string; name: Department['name'] }
      >({
        query: (body) => ({ url: '/departments', method: 'POST', body }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('Department')]),
      }),

      updateDepartment: builder.mutation<Department, { id: string } & Partial<Department>>({
        query: ({ id, ...body }) => ({ url: `/departments/${id}`, method: 'PUT', body }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('Department')]),
      }),

      deleteDepartment: builder.mutation<unknown, string>({
        query: (id) => ({ url: `/departments/${id}`, method: 'DELETE' }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('Department')]),
      }),

      /** Every rank, for the admin editor to filter/group by department client-side. */
      getRanks: builder.query<Paginated<Rank>, void>({
        query: () => ({ url: '/ranks' }),
        providesTags: () => [listTag('Rank')],
      }),

      /** One department's ladder for a category, ordered — the applicant wizard's rank picker. */
      getRankLadder: builder.query<
        Rank[],
        { departmentId: string; certificateCategory: RankCertificateCategory }
      >({
        query: (params) => ({ url: '/ranks/ladder', params }),
        providesTags: () => [listTag('Rank')],
      }),

      createRank: builder.mutation<
        Rank,
        Partial<Rank> & {
          departmentId: string;
          certificateCategory: RankCertificateCategory;
          key: string;
          name: Rank['name'];
        }
      >({
        query: (body) => ({ url: '/ranks', method: 'POST', body }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('Rank')]),
      }),

      updateRank: builder.mutation<Rank, { id: string } & Partial<Rank>>({
        query: ({ id, ...body }) => ({ url: `/ranks/${id}`, method: 'PUT', body }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('Rank')]),
      }),

      deleteRank: builder.mutation<unknown, string>({
        query: (id) => ({ url: `/ranks/${id}`, method: 'DELETE' }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('Rank')]),
      }),

      // -------------------------------------------------------- application
      createApplication: builder.mutation<
        LicenseApplication,
        { licenseType: string; kind?: ApplicationKind; previousLicenseId?: string }
      >({
        query: (body) => ({ url: '/license-applications', method: 'POST', body }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('LicenseApplication')]),
      }),

      discardApplication: builder.mutation<{ deleted: boolean }, string>({
        query: (id) => ({ url: `/license-applications/${id}`, method: 'DELETE' }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('LicenseApplication')]),
      }),

      getMyApplications: builder.query<Paginated<LicenseApplication>, void>({
        query: () => ({ url: '/license-applications/mine' }),
        providesTags: () => [listTag('LicenseApplication')],
      }),

      getApplication: builder.query<ApplicationDetail, string>({
        query: (id) => ({ url: `/license-applications/${id}` }),
        providesTags: (_r, _e, id) => [itemTag('LicenseApplication', id)],
      }),

      patchSection: builder.mutation<
        LicenseApplication,
        { id: string; sectionKey: string; values: Record<string, unknown> }
      >({
        query: ({ id, sectionKey, values }) => ({
          url: `/license-applications/${id}/sections/${sectionKey}`,
          method: 'PATCH',
          body: { values },
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id)],
      }),

      addStaff: builder.mutation<
        ApplicationStaff,
        { id: string; roleKey: string; fullName: string; position?: string; yearsOfExperience?: number }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-applications/${id}/staff`,
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id)],
      }),

      removeStaff: builder.mutation<unknown, { id: string; staffId: string }>({
        query: ({ id, staffId }) => ({
          url: `/license-applications/${id}/staff/${staffId}`,
          method: 'DELETE',
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id)],
      }),

      submitApplication: builder.mutation<LicenseApplication, string>({
        query: (id) => ({ url: `/license-applications/${id}/submit`, method: 'POST' }),
        invalidatesTags: (_r, error, id) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('LicenseApplication'), listTag('ApplicationQueue')],
      }),

      resolveRemark: builder.mutation<unknown, { id: string; remarkId: string }>({
        query: ({ id, remarkId }) => ({
          url: `/license-applications/${id}/remarks/${remarkId}/resolve`,
          method: 'PATCH',
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id)],
      }),

      resubmitApplication: builder.mutation<LicenseApplication, string>({
        query: (id) => ({ url: `/license-applications/${id}/resubmit`, method: 'POST' }),
        invalidatesTags: (_r, error, id) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('LicenseApplication')],
      }),

      // -------------------------------------------------------- attachments
      getAttachments: builder.query<Attachment[], { ownerType: string; ownerId: string }>({
        query: ({ ownerType, ownerId }) => ({
          url: '/attachments',
          params: { ownerType, ownerId, withUrls: true },
        }),
        providesTags: (_r, _e, arg) => [itemTag('Attachment', arg.ownerId)],
      }),

      /**
       * Copies the applicant's personal documents onto an application, for
       * every requirement their vault answers and this application has not
       * already got. Idempotent — a filled slot is never touched.
       */
      fillApplicationDocumentsFromVault: builder.mutation<
        { filled: string[] },
        string
      >({
        query: (applicationId) => ({
          url: `/license-applications/${applicationId}/documents/fill-from-vault`,
          method: 'POST',
        }),
        invalidatesTags: (_r, error, applicationId) =>
          error ? [] : [itemTag('Attachment', applicationId)],
      }),

      /** The same, for a seafarer registration. */
      fillRegistrationDocumentsFromVault: builder.mutation<
        { filled: string[] },
        string
      >({
        query: (registrationId) => ({
          url: `/seafarer-registrations/${registrationId}/documents/fill-from-vault`,
          method: 'POST',
        }),
        invalidatesTags: (_r, error, registrationId) =>
          error ? [] : [itemTag('Attachment', registrationId)],
      }),

      deleteAttachment: builder.mutation<unknown, { attachmentId: string; ownerId: string }>({
        query: ({ attachmentId }) => ({
          url: `/attachments/${attachmentId}`,
          method: 'DELETE',
        }),
        invalidatesTags: (_r, error, { ownerId }) =>
          error ? [] : [itemTag('Attachment', ownerId)],
      }),

      // ----------------------------------------------------------- payments
      initiatePayment: builder.mutation<
        InitiatePaymentResult,
        { id: string; provider?: string; platform?: 'web' | 'mobile'; payerAccount?: string }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-applications/${id}/payments/initiate`,
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id)],
      }),

      getApplicationPayment: builder.query<ApplicationPayment, string>({
        query: (id) => ({ url: `/license-applications/${id}/payments` }),
        providesTags: (_r, _e, id) => [itemTag('LicenseApplication', id)],
      }),

      /** Testing shortcut — only offered when the API reports it enabled. */
      bypassPayment: builder.mutation<
        { status: LicenseStatus; certificateIssued: boolean },
        string
      >({
        query: (id) => ({
          url: `/license-applications/${id}/payments/bypass`,
          method: 'POST',
        }),
        invalidatesTags: (_r, error, id) =>
          error
            ? []
            : [itemTag('LicenseApplication', id), listTag('LicenseApplication'), listTag('License')],
      }),

      getPaymentCapabilities: builder.query<{ bypassEnabled: boolean }, void>({
        query: () => ({ url: '/license-applications/payments/capabilities' }),
      }),

      // ------------------------------------------------------------ licences
      /** The licence register for enforcement officers. */
      getLicenses: builder.query<
        Paginated<IssuedLicense>,
        { search?: string } | void
      >({
        query: (args) => ({
          url: '/licenses',
          params: args?.search ? { search: args.search } : undefined,
        }),
        providesTags: () => [listTag('License')],
      }),

      getMyLicenses: builder.query<Paginated<IssuedLicense>, void>({
        query: () => ({ url: '/licenses/mine' }),
        providesTags: () => [listTag('License')],
      }),

      suspendLicense: builder.mutation<IssuedLicense, { id: string; reason: string }>({
        query: ({ id, reason }) => ({
          url: `/licenses/${id}/suspend`,
          method: 'POST',
          body: { reason },
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('License', id), listTag('License')],
      }),

      revokeLicense: builder.mutation<IssuedLicense, { id: string; reason: string }>({
        query: ({ id, reason }) => ({
          url: `/licenses/${id}/revoke`,
          method: 'POST',
          body: { reason },
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('License', id), listTag('License')],
      }),

      reinstateLicense: builder.mutation<IssuedLicense, { id: string; reason: string }>({
        query: ({ id, reason }) => ({
          url: `/licenses/${id}/reinstate`,
          method: 'POST',
          body: { reason },
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('License', id), listTag('License')],
      }),

      getCertificateUrl: builder.mutation<{ url: string }, string>({
        query: (id) => ({ url: `/licenses/${id}/certificate` }),
      }),

      /**
       * Same download link, backoffice side. Separate endpoint from
       * `getCertificateUrl`: the applicant route only ever hands the
       * certificate to its holder, and an officer reviewing what they just
       * issued is never the holder.
       */
      getCertificateUrlForOfficer: builder.mutation<{ url: string }, string>({
        query: (id) => ({ url: `/licenses/${id}/certificate-backoffice` }),
      }),

      // ------------------------------------------------------------- review
      getQueue: builder.query<Paginated<LicenseApplication>, QueueFilter | void>({
        query: (params) => ({
          url: '/license-application-review/queue',
          params: serialiseQueueFilter(params),
        }),
        providesTags: () => [listTag('ApplicationQueue')],
      }),

      getAssignedToMe: builder.query<
        Paginated<LicenseApplication>,
        QueueFilter | void
      >({
        query: (params) => ({
          url: '/license-application-review/assigned-to-me',
          params: serialiseQueueFilter(params),
        }),
        providesTags: () => [listTag('ApplicationQueue')],
      }),

      /** Every non-draft application — backs the grid's "All" view. */
      getAllApplications: builder.query<
        Paginated<LicenseApplication>,
        QueueFilter | void
      >({
        query: (params) => ({
          url: '/license-application-review/all',
          params: serialiseQueueFilter(params),
        }),
        providesTags: () => [listTag('ApplicationQueue')],
      }),

      /**
       * Counts for the saved-view tabs. Shares the ApplicationQueue tag, so a
       * claim or a decision refreshes the badges along with the list and the
       * numbers can never drift from what the grid is showing.
       */
      getQueueCounts: builder.query<QueueCounts, string | void>({
        query: (licenseTypeKey) => ({
          url: '/license-application-review/counts',
          params: licenseTypeKey ? { licenseTypeKey } : undefined,
        }),
        providesTags: () => [listTag('ApplicationQueue')],
      }),

      getApplicationForReview: builder.query<ApplicationDetail, string>({
        query: (id) => ({ url: `/license-application-review/${id}` }),
        providesTags: (_r, _e, id) => [itemTag('LicenseApplication', id)],
      }),

      claimApplication: builder.mutation<LicenseApplication, string>({
        query: (id) => ({ url: `/license-application-review/${id}/claim`, method: 'POST' }),
        invalidatesTags: (_r, error, id) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      completeReview: builder.mutation<
        LicenseApplication,
        { id: string; capitalAmountVerified?: number; remark?: string }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-application-review/${id}/complete-review`,
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      requestAdjustment: builder.mutation<
        LicenseApplication,
        {
          id: string;
          generalRemark?: string;
          items: { targetType: RemarkTargetType; targetKey: string; remark: string }[];
          /** Officer-edited wording for the applicant notification. */
          notificationBody?: string;
        }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-application-review/${id}/request-adjustment`,
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      approveDocuments: builder.mutation<LicenseApplication, { id: string; remark?: string }>({
        query: ({ id, ...body }) => ({
          url: `/license-application-review/${id}/approve-documents`,
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      /**
       * `capitalAmountVerified` is what the officer confirms against the bank
       * letter. Licence types with a capital threshold reject the approval
       * without it, so it is part of the contract, not an optional extra.
       */
      finalApprove: builder.mutation<
        LicenseApplication,
        { id: string; remark?: string; capitalAmountVerified?: number }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-application-review/${id}/final-approve`,
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      rejectApplication: builder.mutation<
        LicenseApplication,
        { id: string; reason: string; notificationBody?: string }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-application-review/${id}/reject`,
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      /**
       * The examination leg behind one application, as the server reads it.
       *
       * The COC detail page used to infer this from the application status
       * alone, which is how it kept offering "Schedule exam" for a paper the
       * candidate had already sat and passed.
       */
      getExamStateForApplication: builder.query<ExamStateView, string>({
        query: (id) => ({ url: `/exams/applications/${id}/state` }),
        providesTags: (_r, _e, id) => [itemTag('LicenseApplication', id)],
      }),

      // No `getEligibleExams`, `scheduleExam` or `recordExamOutcome`: the
      // endpoints behind them are gone. Sittings are published as master
      // schedules and candidates register for one themselves, and an outcome
      // is what the exam engine computed and a supervisor approved — neither
      // is the COC queue's to write.

      /**
       * A failed candidate asks for another sitting. Re-opens the examination
       * fee (EXAM_FAILED -> EXAM_PAYMENT_PENDING); eligibility was already
       * assessed and paid for on the first attempt.
       */
      retakeExam: builder.mutation<LicenseApplication, string>({
        query: (id) => ({
          url: `/license-applications/${id}/retake`,
          method: 'POST',
        }),
        invalidatesTags: (_r, error, id) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('LicenseApplication')],
      }),

      // ------------------------------------------------- certificate designs
      getLicenseTemplates: builder.query<LicenseTemplate[], string | void>({
        query: (licenseTypeId) => ({
          url: '/license-templates',
          params: licenseTypeId ? { licenseTypeId } : {},
        }),
        providesTags: () => [listTag('LicenseTemplate')],
      }),

      getTemplateVariables: builder.query<TemplateVariable[], void>({
        query: () => ({ url: '/license-templates/variables' }),
      }),

      getBuiltInTemplate: builder.query<{ hbsSource: string }, void>({
        query: () => ({ url: '/license-templates/built-in' }),
      }),

      createLicenseTemplate: builder.mutation<
        LicenseTemplate,
        {
          licenseTypeId: string;
          /** Scopes the draft to one rank's certificate. Omit for the type's default design. */
          rankId?: string | null;
          name: string;
          hbsSource?: string;
          pageOptions?: TemplatePageOptions;
        }
      >({
        query: (body) => ({ url: '/license-templates', method: 'POST', body }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('LicenseTemplate')]),
      }),

      updateLicenseTemplate: builder.mutation<
        LicenseTemplate,
        {
          id: string;
          rankId?: string | null;
          name?: string;
          hbsSource?: string;
          pageOptions?: TemplatePageOptions;
          backgroundUrl?: string;
          logoUrl?: string;
          logoPlacement?: TemplateLogoPlacement;
          fieldPlacements?: TemplateFieldPlacement[];
        }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-templates/${id}`,
          method: 'PATCH',
          body,
        }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('LicenseTemplate')]),
      }),

      publishLicenseTemplate: builder.mutation<LicenseTemplate, string>({
        query: (id) => ({ url: `/license-templates/${id}/publish`, method: 'POST' }),
        // The previously published design is archived by the same call, so the
        // whole list is refetched rather than patching two rows by hand.
        invalidatesTags: (_r, error) => (error ? [] : [listTag('LicenseTemplate')]),
      }),

      archiveLicenseTemplate: builder.mutation<LicenseTemplate, string>({
        query: (id) => ({ url: `/license-templates/${id}/archive`, method: 'POST' }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('LicenseTemplate')]),
      }),

      deleteLicenseTemplate: builder.mutation<unknown, string>({
        query: (id) => ({ url: `/license-templates/${id}`, method: 'DELETE' }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('LicenseTemplate')]),
      }),

      // ------------------------------------------------------ document review
      getDocumentReviews: builder.query<DocumentReview[], string>({
        query: (id) => ({ url: `/license-application-review/${id}/document-reviews` }),
        providesTags: (_r, _e, id) => [itemTag('LicenseApplication', id)],
      }),

      reviewDocument: builder.mutation<
        DocumentReview,
        {
          id: string;
          documentKey: string;
          decision: DocumentDecision;
          reason?: string;
          attachmentId?: string;
        }
      >({
        query: ({ id, documentKey, ...body }) => ({
          url: `/license-application-review/${id}/documents/${encodeURIComponent(documentKey)}/review`,
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id)],
      }),

      clearDocumentReview: builder.mutation<unknown, { id: string; documentKey: string }>({
        query: ({ id, documentKey }) => ({
          url: `/license-application-review/${id}/documents/${encodeURIComponent(documentKey)}/review`,
          method: 'DELETE',
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id)],
      }),

      // ----------------------------------------------------------- saved views
      getSavedViews: builder.query<SavedQueueView[], void>({
        query: () => ({ url: '/license-application-review/saved-views' }),
        providesTags: () => [listTag('SavedView')],
      }),

      createSavedView: builder.mutation<
        SavedQueueView,
        { name: string; queryString: string; isShared?: boolean; isDefault?: boolean }
      >({
        query: (body) => ({
          url: '/license-application-review/saved-views',
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('SavedView')]),
      }),

      deleteSavedView: builder.mutation<unknown, string>({
        query: (viewId) => ({
          url: `/license-application-review/saved-views/${viewId}`,
          method: 'DELETE',
        }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('SavedView')]),
      }),

      // ------------------------------------------------------------- export
      /**
       * The whole filtered result set, not the current page. Lazy so it only
       * runs when the officer actually asks for a file.
       */
      exportApplications: builder.query<ExportResult, QueueFilter | void>({
        query: (params) => ({
          url: '/license-application-review/export',
          params: serialiseQueueFilter(params),
        }),
      }),

      /** Officers the Assign and Escalate dialogs can offer. */
      getAssignableOfficers: builder.query<AssignableOfficer[], void>({
        query: () => ({ url: '/license-application-review/officers' }),
      }),

      // ------------------------------------------------- team-leader dispatch
      /**
       * Handing work out, and handing it back.
       *
       * `assignApplication` below only re-points an application already in
       * flight; these two *start* a stage, because under the push model
       * assignment is how work begins — nothing is claimed from a queue.
       */
      assignInspector: builder.mutation<
        LicenseApplication,
        { id: string; inspectorId: string; remark?: string }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-application-review/${id}/assign-inspector`,
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      reportReview: builder.mutation<
        LicenseApplication,
        { id: string; remark?: string }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-application-review/${id}/report-review`,
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      reportInspection: builder.mutation<
        LicenseApplication,
        { id: string; remark?: string }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-application-review/${id}/report-inspection`,
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      // ----------------------------------------------------- workflow controls
      assignApplication: builder.mutation<
        LicenseApplication,
        { id: string; officerId: string; remark?: string }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-application-review/${id}/assign`,
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      /**
       * Starts the review: the team leader hands the file to an employee.
       * `assign` above only re-points an application already in flight.
       */
      assignReviewer: builder.mutation<
        LicenseApplication,
        { id: string; officerId: string; remark?: string }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-application-review/${id}/assign-reviewer`,
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      holdApplication: builder.mutation<
        LicenseApplication,
        { id: string; reason: string }
      >({
        query: ({ id, reason }) => ({
          url: `/license-application-review/${id}/hold`,
          method: 'POST',
          body: { reason },
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      resumeApplication: builder.mutation<
        LicenseApplication,
        { id: string; remark?: string }
      >({
        query: ({ id, remark }) => ({
          url: `/license-application-review/${id}/resume`,
          method: 'POST',
          body: { remark },
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      escalateApplication: builder.mutation<
        LicenseApplication,
        { id: string; supervisorId: string; reason: string }
      >({
        query: ({ id, ...body }) => ({
          url: `/license-application-review/${id}/escalate`,
          method: 'POST',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      confirmPayment: builder.mutation<LicenseApplication, string>({
        query: (id) => ({
          url: `/license-application-review/${id}/confirm-payment`,
          method: 'POST',
        }),
        invalidatesTags: (_r, error, id) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      scheduleIssuance: builder.mutation<
        LicenseApplication,
        { id: string; scheduledDate: string; scheduledPeriod: IssuancePeriod }
      >({
        query: ({ id, scheduledDate, scheduledPeriod }) => ({
          url: `/license-application-review/${id}/schedule-issuance`,
          method: 'POST',
          body: { scheduledDate, scheduledPeriod },
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('LicenseApplication', id), listTag('ApplicationQueue')],
      }),

      issueCertificate: builder.mutation<LicenseApplication, string>({
        query: (id) => ({
          url: `/license-application-review/${id}/issue-certificate`,
          method: 'POST',
        }),
        // The certificate this writes is what `License` lists read, so the
        // issued register and the holder's own licences refresh with it —
        // without them an officer issues a certificate that nobody's list shows.
        invalidatesTags: (_r, error, id) =>
          error
            ? []
            : [
                itemTag('LicenseApplication', id),
                listTag('ApplicationQueue'),
                listTag('License'),
              ],
      }),

      // ------------------------------------------------------------- pickup
      getPickupOffices: builder.query<PickupOffice[], void>({
        query: () => ({ url: '/pickup/offices' }),
        providesTags: [listTag('PickupOffice')],
      }),

      getPickupSlots: builder.query<
        PickupSlot[],
        { officeId: string; from: string; to: string }
      >({
        query: ({ officeId, from, to }) => ({
          url: `/pickup/offices/${officeId}/slots`,
          params: { from, to },
        }),
      }),

      schedulePickup: builder.mutation<
        PickupAppointment,
        { applicationId: string; officeId: string; date: string; slotStartTime: string }
      >({
        query: (body) => ({ url: '/pickup/appointments', method: 'POST', body }),
        invalidatesTags: (_r, error, { applicationId }) =>
          error
            ? []
            : [
                itemTag('LicenseApplication', applicationId),
                listTag('ApplicationQueue'),
                listTag('PickupAppointment'),
              ],
      }),

      reschedulePickup: builder.mutation<
        PickupAppointment,
        { appointmentId: string; officeId: string; date: string; slotStartTime: string }
      >({
        query: ({ appointmentId, ...body }) => ({
          url: `/pickup/appointments/${appointmentId}/reschedule`,
          method: 'PATCH',
          body,
        }),
        invalidatesTags: (_r, error, { appointmentId }) =>
          error ? [] : [itemTag('PickupAppointment', appointmentId), listTag('PickupAppointment')],
      }),

      getPickupAppointmentsForApplication: builder.query<PickupAppointment[], string>({
        query: (applicationId) => ({
          url: `/pickup/applications/${applicationId}/appointments`,
        }),
        providesTags: (_r, _e, applicationId) => [itemTag('PickupAppointment', applicationId)],
      }),

      getPickupWorklist: builder.query<
        PickupAppointment[],
        { date: string; officeId?: string }
      >({
        query: ({ date, officeId }) => ({
          url: '/pickup/appointments',
          params: officeId ? { date, officeId } : { date },
        }),
        providesTags: [listTag('PickupAppointment')],
      }),

      checkInPickup: builder.mutation<PickupAppointment, string>({
        query: (id) => ({ url: `/pickup/appointments/${id}/check-in`, method: 'PATCH' }),
        invalidatesTags: (_r, error, id) =>
          error ? [] : [itemTag('PickupAppointment', id), listTag('PickupAppointment')],
      }),

      markPickupIssued: builder.mutation<PickupAppointment, string>({
        query: (id) => ({ url: `/pickup/appointments/${id}/issued`, method: 'PATCH' }),
        invalidatesTags: (_r, error, id) =>
          error ? [] : [itemTag('PickupAppointment', id), listTag('PickupAppointment')],
      }),

      markPickupNoShow: builder.mutation<PickupAppointment, string>({
        query: (id) => ({ url: `/pickup/appointments/${id}/no-show`, method: 'PATCH' }),
        invalidatesTags: (_r, error, id) =>
          error ? [] : [itemTag('PickupAppointment', id), listTag('PickupAppointment')],
      }),

      createPickupOffice: builder.mutation<PickupOffice, Partial<PickupOffice>>({
        query: (body) => ({ url: '/pickup/offices', method: 'POST', body }),
        invalidatesTags: [listTag('PickupOffice')],
      }),

      updatePickupOffice: builder.mutation<
        PickupOffice,
        { id: string } & Partial<PickupOffice>
      >({
        query: ({ id, ...body }) => ({
          url: `/pickup/offices/${id}`,
          method: 'PATCH',
          body,
        }),
        invalidatesTags: (_r, error, { id }) =>
          error ? [] : [itemTag('PickupOffice', id), listTag('PickupOffice')],
      }),

      // --------------------------------------------------------- inspection
      scheduleInspection: builder.mutation<
        Inspection,
        {
          applicationId: string;
          scheduledDate: string;
          timeSlot: 'MORNING' | 'AFTERNOON';
          location?: string;
        }
      >({
        query: (body) => ({ url: '/inspections', method: 'POST', body }),
        invalidatesTags: (_r, error, { applicationId }) =>
          error ? [] : [itemTag('LicenseApplication', applicationId), listTag('Inspection')],
      }),

      /** Moves a booked visit: another day, slot, inspector or place. */
      rescheduleInspection: builder.mutation<
        Inspection,
        {
          inspectionId: string;
          applicationId: string;
          scheduledDate: string;
          timeSlot: 'MORNING' | 'AFTERNOON';
          inspectorId?: string;
          location?: string;
          reason?: string;
        }
      >({
        query: ({ inspectionId, scheduledDate, timeSlot, inspectorId, location, reason }) => ({
          url: `/inspections/${inspectionId}/schedule`,
          method: 'PATCH',
          // applicationId is for cache invalidation only; the visit knows its
          // own application.
          body: { scheduledDate, timeSlot, inspectorId, location, reason },
        }),
        invalidatesTags: (_r, error, { applicationId }) =>
          error ? [] : [itemTag('LicenseApplication', applicationId), listTag('Inspection')],
      }),

      getInspections: builder.query<Inspection[], string>({
        query: (applicationId) => ({ url: `/inspections/application/${applicationId}` }),
        providesTags: () => [listTag('Inspection')],
      }),

      recordInspectionResult: builder.mutation<
        Inspection,
        {
          inspectionId: string;
          applicationId: string;
          result: 'PASSED' | 'FAILED';
          findings: string;
          /** Structured per-item outcomes (office, warehouse, vehicles, …). */
          checklist?: {
            key: string;
            label: string;
            outcome: 'PASS' | 'FAIL' | 'NEEDS_CORRECTION';
            note?: string;
          }[];
        }
      >({
        query: ({ inspectionId, result, findings, checklist }) => ({
          url: `/inspections/${inspectionId}/result`,
          method: 'PATCH',
          body: { result, findings, ...(checklist?.length ? { checklist } : {}) },
        }),
        invalidatesTags: (_r, error, { applicationId }) =>
          error ? [] : [itemTag('LicenseApplication', applicationId), listTag('Inspection')],
      }),

      // ------------------------------------------------------ notifications
      getNotifications: builder.query<{ count: number; items: AppNotification[] }, void>({
        query: () => ({ url: '/notifications' }),
        providesTags: () => [listTag('Notification')],
      }),

      getUnseenNotifications: builder.query<{ count: number; items: AppNotification[] }, void>({
        query: () => ({ url: '/notifications/unseen' }),
        providesTags: () => [listTag('Notification')],
      }),

      markNotificationRead: builder.mutation<unknown, string>({
        query: (id) => ({ url: `/notifications/${id}/read`, method: 'PATCH' }),
        invalidatesTags: (_r, error) => (error ? [] : [listTag('Notification')]),
      }),
    }),
    overrideExisting: false,
  });

export const {
  useGetLicenseTypesQuery,
  useGetLicenseCategoriesQuery,
  useUpdateLicenseFeesMutation,
  useUpdateLicenseBehaviorMutation,
  useUpdateFormSchemaMutation,
  useValidateFormSchemaMutation,
  useGetFormSchemaPaletteQuery,
  useGetDocumentRequirementsQuery,
  useGetPersonalDocumentsQuery,
  useCreateDocumentRequirementMutation,
  useUpdateDocumentRequirementMutation,
  useDeleteDocumentRequirementMutation,
  useGetStaffRoleRequirementsQuery,
  useCreateStaffRoleRequirementMutation,
  useUpdateStaffRoleRequirementMutation,
  useDeleteStaffRoleRequirementMutation,
  useGetDepartmentsQuery,
  useGetActiveDepartmentsQuery,
  useCreateDepartmentMutation,
  useUpdateDepartmentMutation,
  useDeleteDepartmentMutation,
  useGetRanksQuery,
  useGetRankLadderQuery,
  useCreateRankMutation,
  useUpdateRankMutation,
  useDeleteRankMutation,
  useUpdateLicenseValidityMutation,
  useGetLicenseTypeRequirementsQuery,
  useCreateApplicationMutation,
  useGetExamStateForApplicationQuery,
  useDiscardApplicationMutation,
  useGetMyApplicationsQuery,
  useGetApplicationQuery,
  useInitiatePaymentMutation,
  useBypassPaymentMutation,
  useGetPaymentCapabilitiesQuery,
  useGetMyLicensesQuery,
  useGetLicensesQuery,
  useGetCertificateUrlMutation,
  useGetCertificateUrlForOfficerMutation,
  useGetApplicationPaymentQuery,
  usePatchSectionMutation,
  useAddStaffMutation,
  useRemoveStaffMutation,
  useSubmitApplicationMutation,
  useResolveRemarkMutation,
  useResubmitApplicationMutation,
  useGetAttachmentsQuery,
  useFillApplicationDocumentsFromVaultMutation,
  useFillRegistrationDocumentsFromVaultMutation,
  useDeleteAttachmentMutation,
  useGetQueueQuery,
  useGetAssignedToMeQuery,
  useGetAllApplicationsQuery,
  useGetQueueCountsQuery,
  useGetLicenseTemplatesQuery,
  useGetTemplateVariablesQuery,
  useGetBuiltInTemplateQuery,
  useCreateLicenseTemplateMutation,
  useUpdateLicenseTemplateMutation,
  usePublishLicenseTemplateMutation,
  useArchiveLicenseTemplateMutation,
  useDeleteLicenseTemplateMutation,
  useGetDocumentReviewsQuery,
  useReviewDocumentMutation,
  useClearDocumentReviewMutation,
  useGetSavedViewsQuery,
  useCreateSavedViewMutation,
  useDeleteSavedViewMutation,
  useLazyExportApplicationsQuery,
  useGetAssignableOfficersQuery,
  useSuspendLicenseMutation,
  useRevokeLicenseMutation,
  useReinstateLicenseMutation,
  useAssignApplicationMutation,
  useAssignReviewerMutation,
  useAssignInspectorMutation,
  useReportReviewMutation,
  useReportInspectionMutation,
  useHoldApplicationMutation,
  useResumeApplicationMutation,
  useEscalateApplicationMutation,
  useGetApplicationForReviewQuery,
  useClaimApplicationMutation,
  useCompleteReviewMutation,
  useRequestAdjustmentMutation,
  useApproveDocumentsMutation,
  useFinalApproveMutation,
  useRejectApplicationMutation,
  useRetakeExamMutation,
  useConfirmPaymentMutation,
  useScheduleIssuanceMutation,
  useIssueCertificateMutation,
  useGetPickupOfficesQuery,
  useGetPickupSlotsQuery,
  useSchedulePickupMutation,
  useReschedulePickupMutation,
  useGetPickupAppointmentsForApplicationQuery,
  useGetPickupWorklistQuery,
  useCheckInPickupMutation,
  useMarkPickupIssuedMutation,
  useMarkPickupNoShowMutation,
  useCreatePickupOfficeMutation,
  useUpdatePickupOfficeMutation,
  useScheduleInspectionMutation,
  useRescheduleInspectionMutation,
  useGetInspectionsQuery,
  useRecordInspectionResultMutation,
  useGetNotificationsQuery,
  useGetUnseenNotificationsQuery,
  useMarkNotificationReadMutation,
  useGetMyOperatorTypesQuery,
  useUpdateMyOperatorTypesMutation,
} = licensingApi;
