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

export const ITEM_PRIORITIES = ["LOW", "MEDIUM", "HIGH", "URGENT"] as const;
export type ItemPriority = (typeof ITEM_PRIORITIES)[number];
export function isItemPriority(value: string | undefined): value is ItemPriority {
  return value !== undefined && ITEM_PRIORITIES.some((p) => p === value);
}

const builtins = (params: { userType?: TailorAnyDBType }) => {
  const assigneeId = db
    .uuid({ optional: true })
    .description("Foreign key to User assigned to the item");
  return {
    pipelineId: db
      .uuid()
      .relation({
        type: "n-1",
        toward: { type: pipeline },
        backward: "items",
      })
      .description("ID of the pipeline this item belongs to"),
    stageId: db
      .uuid()
      .relation({
        type: "n-1",
        toward: { type: pipelineStage },
        backward: "items",
      })
      .description("Current stage ID (changed via drag & drop)"),
    title: db.string().description("PipelineItem title"),
    description: db
      .string({ optional: true })
      .description("Detailed description or notes for the item"),
    assigneeId: params.userType
      ? assigneeId.relation({
          type: "n-1",
          toward: { type: params.userType },
          backward: "assignedPipelineItems",
        })
      : assigneeId,
    priority: db.enum(ITEM_PRIORITIES).description("Priority. LOW / MEDIUM / HIGH / URGENT"),
    dueDate: db.date({ optional: true }).description("Due date"),
    // Reorder and list queries both filter by stageId and order by position;
    // when items per stage grow large, add a composite (stageId, position)
    // index at the platform level to keep the renumbering writes cheap.
    position: db.int().description("Display order within the stage (top to bottom, 1-based)"),
    itemNumber: db
      .int()
      .description(
        "Sequential number within the pipeline. Auto-assigned like a GitHub issue number.",
      ),
    lifecycle: db
      .enum(pipelineItemLifecycle.states)
      .description(
        "PipelineItem lifecycle. DRAFT = visible only to the creator and hidden from operational lists, OPEN = published and in progress, CLOSED = explicitly closed. Independent of the PipelineStage category (DONE/CANCELED); transitioning to CLOSED only happens via an explicit ClosePipelineItem call. Approval workflows are not carried on the PipelineItem; the owning domain holds them as needed.",
      ),
  };
};

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

export interface CreatePipelineItemTypeParams<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 createPipelineItemType<const F extends Record<string, TailorAnyDBField>>(
  params: CreatePipelineItemTypeParams<F>,
) {
  return db
    .type("PipelineItem", {
      ...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(
      "Generic work item. A unit of work placed on a PipelineStage within a Pipeline; a general-purpose progress-tracking entity that domain modules can link to via foreign key.",
    )
    .permission(defaultPermission)
    .gqlPermission(defaultGqlPermission);
}

export const pipelineItem = createPipelineItemType({});
