import { UserRoleMap } from "../../../core/enums";
import { paginationModel } from "../../../core/pagination.utils";
import { encodeCursor } from "../../../core/string.utils";
import z from "zod";

const usernameModel = z
  .string()
  .min(2)
  .max(128)
  .regex(/^[a-zA-Z0-9_-]+$/, {
    message:
      "Username can only contain letters, numbers, underscores, and hyphens.",
  });

export const emailModel = z.string().email().min(5).max(128);

export const discountPercentModel = z
  .number()
  .min(0)
  .max(100)
  .default(0)
  .nullish();

const roleModel = z.enum(Object.values(UserRoleMap) as [string, ...string[]]);

export const createUserPayloadModel = z.object({
  username: usernameModel,
  email: emailModel,
  password: z.string().min(8).max(128),
  role: roleModel,
});

export type CreateUserPayloadModel = z.infer<typeof createUserPayloadModel>;

export type GetUserByPayload = { id?: string; username?: string };

export const updateUserInfoInputModel = z.object({
  email: emailModel,
  username: usernameModel,
  discountPercent: discountPercentModel,
  banned: z.boolean().nullable().optional(),
});
export type UpdateUserInfoInputModel = z.infer<typeof updateUserInfoInputModel>;

export const userModel = updateUserInfoInputModel.merge(
  z.object({
    id: z.coerce.string(),
    role: roleModel.nullable().optional(),
    discountPercent: discountPercentModel,
    createdAt: z.coerce.date(),
    updatedAt: z.coerce.date(),
    parentId: z.coerce.string().nullable().optional(),
  }),
);
export type UserModel = z.infer<typeof userModel>;

export const limitedUserModel = userModel.pick({
  id: true,
  username: true,
  email: true,
});

export type LimitedUserModel = z.infer<typeof limitedUserModel>;

export const userFullModel = userModel.merge(
  z.object({
    banned: z.boolean(),
    banReason: z.string().optional(),
    banExpires: z.date().optional(),
    twoFactorEnabled: z.boolean().default(false),
  }),
);
export type UserFullModel = z.infer<typeof userFullModel>;

export const usersCursorModel = z.object({
  firstItemUsername: z.string(),
  lastItemUsername: z.string(),
  query: z.string().default(""),
});
export type UsersCursorModel = z.infer<typeof usersCursorModel>;
export function getDefaultUsersCursor() {
  return usersCursorModel.parse({
    firstItemUsername: "",
    lastItemUsername: "",
    query: "",
  });
}

export const paginatedUserModel = paginationModel.merge(
  z.object({ data: z.array(userModel) }),
);
export type PaginatedUserModel = z.infer<typeof paginatedUserModel>;
export function getDefaultPaginatedUserModel(): PaginatedUserModel {
  return {
    data: [],
    cursor: encodeCursor<UsersCursorModel>(getDefaultUsersCursor()),
    limit: 20,
    asc: true,
    totalItemCount: 0,
    totalPages: 0,
    page: 0,
  };
}
