import {
  type CommonReservedFields,
  type NoReservedFields,
  defaultGqlPermission,
  defaultPermission,
} from "@tailor-platform/erp-kit/core";
import { db, type TailorAnyDBField, type TailorAnyDBType } from "@tailor-platform/sdk";
import { pipelineItem } from "./pipelineItem";

export const PIPELINE_ITEM_CHANGE_TYPES = ["CONTENT", "FIELD", "LIFECYCLE", "LABEL"] as const;

export const PIPELINE_ITEM_CHANGE_FIELD_NAMES = [
  "TITLE",
  "DESCRIPTION",
  "ASSIGNEE",
  "PRIORITY",
  "DUE_DATE",
  "LIFECYCLE",
  "LABEL",
] as const;

const builtins = (params: { userType?: TailorAnyDBType }) => {
  const changedByUserId = db.uuid().description("Foreign key to User who made the change");
  return {
    itemId: db
      .uuid()
      .relation({
        type: "n-1",
        toward: { type: pipelineItem },
        backward: "changes",
      })
      .description("ID of the item that changed"),
    changedByUserId: params.userType
      ? changedByUserId.relation({
          type: "n-1",
          toward: { type: params.userType },
          backward: "pipelineItemChanges",
        })
      : changedByUserId,
    changeType: db
      .enum(PIPELINE_ITEM_CHANGE_TYPES)
      .description(
        "Change category. CONTENT = title/description / FIELD = assignee/priority/dueDate / LIFECYCLE = DRAFT→OPEN→CLOSED / LABEL = label attach/detach",
      ),
    fieldName: db
      .enum(PIPELINE_ITEM_CHANGE_FIELD_NAMES)
      .description(
        "Name of the changed field. TITLE / DESCRIPTION / ASSIGNEE / PRIORITY / DUE_DATE / LIFECYCLE / LABEL",
      ),
    prevValue: db
      .string({ optional: true })
      .description(
        "Value before the change (one of uuid / enum / ISO date / string depending on the field type; null if it was previously unset).",
      ),
    newValue: db
      .string({ optional: true })
      .description(
        "Value after the change (one of uuid / enum / ISO date / string depending on the field type; null if it was cleared).",
      ),
  };
};

type ReservedFields = keyof ReturnType<typeof builtins> | CommonReservedFields;

export interface CreatePipelineItemChangeTypeParams<F extends Record<string, TailorAnyDBField>> {
  fields?: NoReservedFields<F, ReservedFields>;
  /** Related User type (user-management module). The instance wired by the consuming app is injected here. */
  userType?: TailorAnyDBType;
}

export function createPipelineItemChangeType<const F extends Record<string, TailorAnyDBField>>(
  params: CreatePipelineItemChangeTypeParams<F>,
) {
  return db
    .type("PipelineItemChange", {
      ...builtins(params),
      // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- mirrors erp-kit's createXxxType factories; field bag is constrained but TS can't prove F is concrete
      ...((params.fields ?? {}) as F),
      ...db.fields.timestamps(),
    })
    .description(
      "PipelineItem change history (other than PipelineStageTransition). An append-only log where one row is added per field that changed in a single operation.",
    )
    .permission(defaultPermission)
    .gqlPermission(defaultGqlPermission);
}

export const pipelineItemChange = createPipelineItemChangeType({});
