import { z } from "zod";
import { BOOKING_TIME_PATTERN } from "@rental/shared";

const emptyToNull = (value: unknown) => {
  if (typeof value === "string" && value.trim() === "") {
    return null;
  }

  return value;
};

export const bookingParamsSchema = z.object({
  id: z.string().uuid()
});

export const bookingQuerySchema = z.object({
  itemId: z.string().uuid().optional(),
  categoryId: z.string().uuid().optional(),
  customerName: z.string().optional(),
  from: z.string().optional(),
  to: z.string().optional()
});

const bookingTimeSchema = z.preprocess(
  emptyToNull,
  z.string().regex(BOOKING_TIME_PATTERN, "Time must match HH:MM").nullable().optional()
);

const bookingPriceSchema = z.preprocess((value) => {
  if (value === null || value === undefined) {
    return null;
  }

  if (typeof value === "string") {
    const normalized = value.trim().replace(",", ".");

    if (normalized === "") {
      return null;
    }

    return Number(normalized);
  }

  return value;
}, z.number().finite().min(0).max(100_000_000).nullable().optional());

export const bookingCreateSchema = z
  .object({
    equipmentItemIds: z.array(z.string().uuid()).min(1),
    customerName: z.string().min(2).max(180),
    orderNumber: z.preprocess(emptyToNull, z.string().max(120).nullable().optional()),
    projectNumber: z.preprocess(emptyToNull, z.string().max(120).nullable().optional()),
    projectName: z.preprocess(emptyToNull, z.string().max(180).nullable().optional()),
    notes: z.preprocess(emptyToNull, z.string().max(2000).nullable().optional()),
    startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
    endDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
    startTime: bookingTimeSchema,
    endTime: bookingTimeSchema,
    dayRate: bookingPriceSchema,
    totalPrice: bookingPriceSchema,
    useSuggestedItemRates: z.boolean().optional().default(false),
    allowConflict: z.boolean().optional().default(false)
  })
  .refine((value) => value.startDate <= value.endDate, {
    message: "startDate must be before or equal to endDate",
    path: ["endDate"]
  })
  .refine((value) => {
    if (value.startDate !== value.endDate || !value.startTime || !value.endTime) {
      return true;
    }

    return value.startTime < value.endTime;
  }, {
    message: "On the same day, endTime must be later than startTime",
    path: ["endTime"]
  });

export const bookingPatchSchema = z
  .object({
    equipmentItemId: z.string().uuid(),
    customerName: z.string().min(2).max(180),
    orderNumber: z.preprocess(emptyToNull, z.string().max(120).nullable().optional()),
    projectNumber: z.preprocess(emptyToNull, z.string().max(120).nullable().optional()),
    projectName: z.preprocess(emptyToNull, z.string().max(180).nullable().optional()),
    notes: z.preprocess(emptyToNull, z.string().max(2000).nullable().optional()),
    startDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
    endDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
    startTime: bookingTimeSchema,
    endTime: bookingTimeSchema,
    dayRate: bookingPriceSchema,
    totalPrice: bookingPriceSchema,
    allowConflict: z.boolean().optional().default(false)
  })
  .refine((value) => value.startDate <= value.endDate, {
    message: "startDate must be before or equal to endDate",
    path: ["endDate"]
  })
  .refine((value) => {
    if (value.startDate !== value.endDate || !value.startTime || !value.endTime) {
      return true;
    }

    return value.startTime < value.endTime;
  }, {
    message: "On the same day, endTime must be later than startTime",
    path: ["endTime"]
  });

export const bookingScopePatchSchema = z.object({
  equipmentItemIds: z.array(z.string().uuid()).min(1),
  allowConflict: z.boolean().optional().default(false)
});
