import { type EmptyFields, type FieldsToInsertable } from "@tailor-platform/erp-kit/core";
import { type TailorAnyDBField } from "@tailor-platform/sdk";
import type { defineModule as defineUserManagementModule } from "../user-management";
import { attachLabelToPipelineItem } from "./command/attachLabelToPipelineItem.generated";
import { closePipelineItem } from "./command/closePipelineItem.generated";
import { createPipeline } from "./command/createPipeline.generated";
import { createPipelineItem } from "./command/createPipelineItem.generated";
import { createPipelineItemComment } from "./command/createPipelineItemComment.generated";
import { createPipelineLabel } from "./command/createPipelineLabel.generated";
import { createPipelineStage } from "./command/createPipelineStage.generated";
import { deactivatePipeline } from "./command/deactivatePipeline.generated";
import { deletePipelineItem } from "./command/deletePipelineItem.generated";
import { deletePipelineItemComment } from "./command/deletePipelineItemComment.generated";
import { deletePipelineLabel } from "./command/deletePipelineLabel.generated";
import { deletePipelineStage } from "./command/deletePipelineStage.generated";
import { detachLabelFromPipelineItem } from "./command/detachLabelFromPipelineItem.generated";
import { movePipelineItem } from "./command/movePipelineItem.generated";
import { openPipelineItem } from "./command/openPipelineItem.generated";
import { reopenPipelineItem } from "./command/reopenPipelineItem.generated";
import { reorderPipelineItem } from "./command/reorderPipelineItem.generated";
import { reorderPipelineStage } from "./command/reorderPipelineStage.generated";
import { updatePipeline } from "./command/updatePipeline.generated";
import { updatePipelineItem } from "./command/updatePipelineItem.generated";
import { updatePipelineItemComment } from "./command/updatePipelineItemComment.generated";
import { updatePipelineLabel } from "./command/updatePipelineLabel.generated";
import { updatePipelineStage } from "./command/updatePipelineStage.generated";
import { createPipelineType, type CreatePipelineTypeParams } from "./db/pipeline";
import { createPipelineItemType, type CreatePipelineItemTypeParams } from "./db/pipelineItem";
import {
  createPipelineItemChangeType,
  type CreatePipelineItemChangeTypeParams,
} from "./db/pipelineItemChange";
import {
  createPipelineItemCommentType,
  type CreatePipelineItemCommentTypeParams,
} from "./db/pipelineItemComment";
import {
  createPipelineItemLabelType,
  type CreatePipelineItemLabelTypeParams,
} from "./db/pipelineItemLabel";
import { createPipelineLabelType, type CreatePipelineLabelTypeParams } from "./db/pipelineLabel";
import { createPipelineStageType, type CreatePipelineStageTypeParams } from "./db/pipelineStage";
import {
  createPipelineStageTransitionType,
  type CreatePipelineStageTransitionTypeParams,
} from "./db/pipelineStageTransition";
import { listPipelineItemCommentsByItem } from "./query/listPipelineItemCommentsByItem.generated";
import { listPipelineItemsByStage } from "./query/listPipelineItemsByStage.generated";
import { listPipelineLabelsByItem } from "./query/listPipelineLabelsByItem.generated";
import { listPipelineLabelsByPipeline } from "./query/listPipelineLabelsByPipeline.generated";
import { listPipelineStagesByPipeline } from "./query/listPipelineStagesByPipeline.generated";
import { listPipelineStageTransitionsByItem } from "./query/listPipelineStageTransitionsByItem.generated";

type UserManagementModule = ReturnType<typeof defineUserManagementModule>;
type UserManagementDB = UserManagementModule["db"];

export interface DefineModuleParams<
  PipelineF extends Record<string, TailorAnyDBField>,
  PipelineStageF extends Record<string, TailorAnyDBField>,
  PipelineItemF extends Record<string, TailorAnyDBField>,
  PipelineItemChangeF extends Record<string, TailorAnyDBField>,
  PipelineItemCommentF extends Record<string, TailorAnyDBField>,
  PipelineStageTransitionF extends Record<string, TailorAnyDBField>,
  PipelineLabelF extends Record<string, TailorAnyDBField>,
  PipelineItemLabelF extends Record<string, TailorAnyDBField>,
> {
  /**
   * Allowed `pipelineType` vocabulary, declared by the consuming app
   * (e.g. ["TASK_BOARD", "SUPPORT_QUEUE"]). The module itself is
   * type-agnostic; when omitted, createPipeline accepts any non-empty string.
   */
  pipelineTypes?: readonly string[];
  // userType is Omit-ed: defineModule injects the real User type, so a consumer value would be discarded.
  pipeline?: Omit<CreatePipelineTypeParams<PipelineF>, "userType">;
  pipelineStage?: CreatePipelineStageTypeParams<PipelineStageF>;
  pipelineItem?: Omit<CreatePipelineItemTypeParams<PipelineItemF>, "userType">;
  pipelineItemChange?: Omit<CreatePipelineItemChangeTypeParams<PipelineItemChangeF>, "userType">;
  pipelineItemComment?: Omit<CreatePipelineItemCommentTypeParams<PipelineItemCommentF>, "userType">;
  pipelineStageTransition?: Omit<
    CreatePipelineStageTransitionTypeParams<PipelineStageTransitionF>,
    "userType"
  >;
  pipelineLabel?: CreatePipelineLabelTypeParams<PipelineLabelF>;
  pipelineItemLabel?: CreatePipelineItemLabelTypeParams<PipelineItemLabelF>;
  userManagement: {
    db: {
      user: UserManagementDB["user"];
    };
    /**
     * Optional user-existence check used by createPipeline (createdByUserId) and
     * createPipelineItem / updatePipelineItem (assigneeId). When provided,
     * referencing an unknown user fails with USER_NOT_FOUND.
     */
    getUser?: (id: string) => Promise<{ id: string } | null>;
  };
}

/* @__NO_SIDE_EFFECTS__ */
export const defineModule = <
  const PipelineF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PipelineStageF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PipelineItemF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PipelineItemChangeF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PipelineItemCommentF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PipelineStageTransitionF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PipelineLabelF extends Record<string, TailorAnyDBField> = EmptyFields,
  const PipelineItemLabelF extends Record<string, TailorAnyDBField> = EmptyFields,
>(
  params: DefineModuleParams<
    PipelineF,
    PipelineStageF,
    PipelineItemF,
    PipelineItemChangeF,
    PipelineItemCommentF,
    PipelineStageTransitionF,
    PipelineLabelF,
    PipelineItemLabelF
  >,
) => {
  const userType = params.userManagement.db.user;
  const pipeline = createPipelineType({ ...params.pipeline, userType });
  const pipelineStage = createPipelineStageType(params.pipelineStage ?? {});
  const pipelineItem = createPipelineItemType({ ...params.pipelineItem, userType });
  const pipelineLabel = createPipelineLabelType(params.pipelineLabel ?? {});
  const pipelineItemChange = createPipelineItemChangeType({
    ...params.pipelineItemChange,
    userType,
  });
  const pipelineItemComment = createPipelineItemCommentType({
    ...params.pipelineItemComment,
    userType,
  });
  const pipelineStageTransition = createPipelineStageTransitionType({
    ...params.pipelineStageTransition,
    userType,
  });
  const pipelineItemLabel = createPipelineItemLabelType(params.pipelineItemLabel ?? {});

  return {
    db: {
      pipeline,
      pipelineStage,
      pipelineItem,
      pipelineItemChange,
      pipelineItemComment,
      pipelineStageTransition,
      pipelineLabel,
      pipelineItemLabel,
    },
    commands: {
      createPipeline: createPipeline<FieldsToInsertable<PipelineF>>({
        allowedPipelineTypes: params.pipelineTypes,
        getUser: params.userManagement.getUser,
      }),
      updatePipeline: updatePipeline<FieldsToInsertable<PipelineF>>(),
      deactivatePipeline: deactivatePipeline(),
      createPipelineStage: createPipelineStage<FieldsToInsertable<PipelineStageF>>(),
      updatePipelineStage: updatePipelineStage<FieldsToInsertable<PipelineStageF>>(),
      deletePipelineStage: deletePipelineStage(),
      reorderPipelineStage: reorderPipelineStage(),
      createPipelineItem: createPipelineItem<FieldsToInsertable<PipelineItemF>>({
        getUser: params.userManagement.getUser,
      }),
      updatePipelineItem: updatePipelineItem<FieldsToInsertable<PipelineItemF>>({
        getUser: params.userManagement.getUser,
      }),
      movePipelineItem: movePipelineItem(),
      reorderPipelineItem: reorderPipelineItem(),
      deletePipelineItem: deletePipelineItem(),
      closePipelineItem: closePipelineItem(),
      openPipelineItem: openPipelineItem(),
      reopenPipelineItem: reopenPipelineItem(),
      createPipelineItemComment:
        createPipelineItemComment<FieldsToInsertable<PipelineItemCommentF>>(),
      updatePipelineItemComment:
        updatePipelineItemComment<FieldsToInsertable<PipelineItemCommentF>>(),
      deletePipelineItemComment: deletePipelineItemComment(),
      createPipelineLabel: createPipelineLabel<FieldsToInsertable<PipelineLabelF>>(),
      updatePipelineLabel: updatePipelineLabel<FieldsToInsertable<PipelineLabelF>>(),
      deletePipelineLabel: deletePipelineLabel(),
      attachLabelToPipelineItem:
        attachLabelToPipelineItem<FieldsToInsertable<PipelineItemLabelF>>(),
      detachLabelFromPipelineItem: detachLabelFromPipelineItem(),
    },
    queries: {
      listPipelineStagesByPipeline,
      listPipelineItemsByStage,
      listPipelineStageTransitionsByItem,
      listPipelineItemCommentsByItem,
      listPipelineLabelsByPipeline,
      listPipelineLabelsByItem,
    },
  };
};

/**
 * The module shape with no consumer field extensions — what `defineModule`
 * returns for a plain wiring. Consumer modules should derive db/command/query
 * types from this alias rather than `ReturnType<typeof defineModule>`, which
 * erases the field generics to their constraints instead of their
 * `EmptyFields` defaults.
 */
export type PipelineManagementModule = ReturnType<
  typeof defineModule<
    EmptyFields,
    EmptyFields,
    EmptyFields,
    EmptyFields,
    EmptyFields,
    EmptyFields,
    EmptyFields,
    EmptyFields
  >
>;
