import { z } from "zod";
import { EntityId, TaskStatus } from "./entities";

export const CreateTaskRequest = z.object({
  title: z.string().min(1).max(200),
  description: z.string().max(10_000).optional(),
  assigneeUserId: EntityId.nullable().optional(),
  dueAt: z.string().datetime().nullable().optional(),
  channelId: EntityId.nullable().optional(),
  status: TaskStatus.optional(), // defaults to "todo" server-side
});
export type CreateTaskRequest = z.infer<typeof CreateTaskRequest>;

// Partial update — everything optional; an explicit `null` clears a nullable
// field, `undefined` (an omitted key) leaves it untouched. Same idiom
// UpdateChannelRequest uses, extended to the nullable fields tasks have.
export const UpdateTaskRequest = z.object({
  title: z.string().min(1).max(200).optional(),
  description: z.string().max(10_000).nullable().optional(),
  dueAt: z.string().datetime().nullable().optional(),
  channelId: EntityId.nullable().optional(),
});
export type UpdateTaskRequest = z.infer<typeof UpdateTaskRequest>;

export const AssignTaskRequest = z.object({ assigneeUserId: EntityId.nullable() });
export type AssignTaskRequest = z.infer<typeof AssignTaskRequest>;

export const UpdateTaskStatusRequest = z.object({ status: TaskStatus });
export type UpdateTaskStatusRequest = z.infer<typeof UpdateTaskStatusRequest>;

// GET /workspaces/:workspaceId/tasks query filters. z.coerce throughout since
// query-string params always arrive as strings (same idiom as ScrollbackQuery).
export const ListTasksQuery = z.object({
  status: TaskStatus.optional(),
  assigneeUserId: EntityId.optional(),
  channelId: EntityId.optional(),
  // NOT z.coerce.boolean() — that maps every non-empty string (including
  // "false" and "0") to true, so `?mine=false` would silently filter.
  mine: z
    .enum(["true", "false", "1", "0"])
    .transform((v) => v === "true" || v === "1")
    .optional(),
  /** When true, only soft-deleted tasks; otherwise only active ones. */
  deleted: z
    .enum(["true", "false", "1", "0"])
    .transform((v) => v === "true" || v === "1")
    .optional(),
  dueBefore: z.string().datetime().optional(),
  dueAfter: z.string().datetime().optional(),
  limit: z.coerce.number().int().min(1).max(200).default(100),
});
export type ListTasksQuery = z.infer<typeof ListTasksQuery>;

// Both fields optional — omitted ones default to the source message's own
// text (see createTaskFromMessage), letting the modal's edited title/
// description win only when the user actually changed them.
export const CreateTaskFromMessageRequest = z.object({
  title: z.string().min(1).max(200).optional(),
  description: z.string().max(10_000).optional(),
  assigneeUserId: EntityId.nullable().optional(),
  dueAt: z.string().datetime().nullable().optional(),
});
export type CreateTaskFromMessageRequest = z.infer<typeof CreateTaskFromMessageRequest>;
