import { z } from 'zod';

export function canCompleteTask(dueDate: number): boolean {
  if (dueDate === 0) return true;
  const now = new Date();
  const endOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999).getTime();
  return dueDate <= endOfToday;
}

export const categoryCreateSchema = z.object({
  name: z.string().min(1).max(50),
  color: z.string().regex(/^#[0-9A-Fa-f]{6}$/),
  order: z.number().int().min(0).optional(),
});

export const categoryUpdateSchema = z.object({
  name: z.string().min(1).max(50).optional(),
  color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
  order: z.number().int().min(0).optional(),
});

export const repeatSchema = z.enum(['none', 'daily', 'weekly', 'monthly', 'custom']);

// The client stores '' / 0 for unset repeat values; normalize them so stale or
// legacy rows never fail validation on push.
export const repeatFieldSchema = repeatSchema
  .or(z.literal(''))
  .transform((v) => (v === '' ? 'none' : v))
  .optional();
export const repeatIntervalFieldSchema = z
  .number()
  .int()
  .min(0)
  .max(30)
  .transform((v) => Math.max(1, v))
  .optional();

export const taskCreateSchema = z.object({
  title: z.string().min(1).max(100),
  description: z.string().max(1000).optional(),
  categoryId: z.string().max(100).transform((v) => (v === '' ? null : v)).nullable(),
  tags: z.string().max(500).optional(),
  priority: z.enum(['none', 'low', 'medium', 'high', 'critical']).optional(),
  dueDate: z.number().int().min(0).optional(),
  dueTime: z.string().regex(/^([01]\d|2[0-3]):([0-5]\d)$/).optional().or(z.literal('')).transform(v => v ?? ''),
  endTime: z.string().regex(/^([01]\d|2[0-3]):([0-5]\d)$/).optional().or(z.literal('')),
  allDay: z.boolean().optional(),
  repeat: repeatFieldSchema,
  repeatInterval: repeatIntervalFieldSchema,
  repeatDays: z.string().max(20).optional(),
  seriesId: z.string().max(50).optional(),
  reminder: z.enum(['none', 'at_time', '15', '30', '60', '120', '1440']).optional(),
  reminders: z.string().max(100).optional(),
  assigneeId: z.string().nullable().optional(),
  completedAt: z.number().int().min(0).nullable().optional(),
  subtasks: z.array(z.object({ title: z.string().min(1).max(100), categoryId: z.string().max(100).nullable().optional().transform((v) => (v === '' ? null : v)) })).optional(),
});

export const taskUpdateSchema = z.object({
  title: z.string().min(1).max(100).optional(),
  description: z.string().max(1000).optional(),
  categoryId: z.string().max(100).nullable().optional().transform((v) => (v === '' ? null : v)),
  tags: z.string().max(500).optional(),
  priority: z.enum(['none', 'low', 'medium', 'high', 'critical']).optional(),
  completed: z.boolean().optional(),
  dueDate: z.number().int().min(0).optional(),
  dueTime: z.string().regex(/^([01]\d|2[0-3]):([0-5]\d)$/).optional().or(z.literal('')).transform(v => v === '' ? undefined : v),
  endTime: z.string().regex(/^([01]\d|2[0-3]):([0-5]\d)$/).optional(),
  reminders: z.string().max(100).optional(),
  assigneeId: z.string().nullable().optional(),
  completedAt: z.number().int().min(0).nullable().optional(),
});

export const subtaskCreateSchema = z.object({
  title: z.string().min(1).max(100),
  description: z.string().max(1000).optional(),
  categoryId: z.string().max(100).nullable().optional().transform((v) => (v === '' ? null : v)),
  priority: z.enum(['none', 'low', 'medium', 'high', 'critical']).optional(),
  dueDate: z.number().int().min(0).optional(),
  dueTime: z.string().regex(/^([01]\d|2[0-3]):([0-5]\d)$/).optional().or(z.literal('')).transform(v => v ?? ''),
  endTime: z.string().regex(/^([01]\d|2[0-3]):([0-5]\d)$/).optional().or(z.literal('')),
  allDay: z.boolean().optional(),
  repeat: repeatFieldSchema,
  repeatInterval: repeatIntervalFieldSchema,
  repeatDays: z.string().max(20).optional(),
  seriesId: z.string().max(50).optional(),
  reminder: z.enum(['none', 'at_time', '15', '30', '60', '120', '1440']).optional(),
  reminders: z.string().max(100).optional(),
  assigneeId: z.string().nullable().optional(),
  order: z.number().int().min(0).optional(),
  parentSubtaskId: z.string().nullable().optional(),
});

export const subtaskUpdateSchema = z.object({
  title: z.string().min(1).max(100).optional(),
  description: z.string().max(1000).optional(),
  categoryId: z.string().max(100).nullable().optional().transform((v) => (v === '' ? null : v)),
  priority: z.enum(['none', 'low', 'medium', 'high', 'critical']).optional(),
  completed: z.boolean().optional(),
  dueDate: z.number().int().min(0).optional(),
  dueTime: z.string().regex(/^([01]\d|2[0-3]):([0-5]\d)$/).optional().or(z.literal('')).transform(v => v === '' ? undefined : v),
  endTime: z.string().regex(/^([01]\d|2[0-3]):([0-5]\d)$/).optional(),
  allDay: z.boolean().optional(),
  repeat: repeatFieldSchema,
  repeatInterval: repeatIntervalFieldSchema,
  repeatDays: z.string().max(20).optional(),
  seriesId: z.string().max(50).optional(),
  reminder: z.enum(['none', 'at_time', '15', '30', '60', '120', '1440']).optional(),
  reminders: z.string().max(100).optional(),
  assigneeId: z.string().nullable().optional(),
  order: z.number().int().min(0).optional(),
  parentSubtaskId: z.string().nullable().optional(),
});

export const userSettingsSchema = z.object({
  darkMode: z.boolean().optional(),
  notifications: z.boolean().optional(),
  reminderTime: z.string().regex(/^([01]\d|2[0-3]):([0-5]\d)$/).optional(),
  defaultCategory: z.string().optional().nullable(),
  sortBy: z.enum(['dueDate', 'priority', 'title', 'createdAt']).optional(),
  sortOrder: z.enum(['asc', 'desc']).optional(),
});

export const repeatProfileSchema = z.object({
  name: z.string().min(1).max(50),
  repeat: repeatSchema,
  repeatInterval: z.number().int().min(1).max(30),
  repeatDays: z.string().max(20),
});

export const repeatProfileUpdateSchema = z.object({
  name: z.string().min(1).max(50).optional(),
  repeat: repeatSchema.optional(),
  repeatInterval: z.number().int().min(1).max(30).optional(),
  repeatDays: z.string().max(20).optional(),
});

export const searchQuerySchema = z.object({
  q: z.string().min(1).max(50),
});

export const friendRequestSchema = z.object({
  username: z.string().min(1).max(50),
});

export const friendshipSchema = z.object({
  id: z.string(),
  userId: z.string(),
  friendId: z.string(),
  status: z.enum(['pending', 'accepted']),
  createdAt: z.number(),
  updatedAt: z.number(),
});

export const tombstoneSchema = z.object({
  entity: z.enum(['categories', 'tasks', 'subtasks', 'repeatProfiles', 'friendships']),
  id: z.string(),
  updatedAt: z.number().int().min(0),
});

export const pushChangesSchema = z.object({
  changes: z.object({
    categories: z.array(categoryCreateSchema.extend({ id: z.string(), createdAt: z.number(), updatedAt: z.number() })).optional(),
    tasks: z.array(taskCreateSchema.extend({ id: z.string(), completed: z.boolean(), createdAt: z.number(), updatedAt: z.number(), completedAt: z.number().int().min(0).nullable().optional() })).optional(),
    subtasks: z.array(subtaskCreateSchema.extend({ id: z.string(), taskId: z.string(), completed: z.boolean(), createdAt: z.number(), updatedAt: z.number() })).optional(),
    repeatProfiles: z.array(repeatProfileSchema.extend({ id: z.string(), createdAt: z.number(), updatedAt: z.number() })).optional(),
    friendships: z.array(friendshipSchema).optional(),
  }),
  deleted: z.array(tombstoneSchema).optional(),
  lastPulledAt: z.number().int().min(0),
});

export const syncQuerySchema = z.object({
  since: z.string().transform(Number).pipe(z.number().int().min(0)),
});

export const taskQuerySchema = z.object({
  categoryId: z.string().optional(),
  completed: z.string().transform(v => v === 'true').optional(),
  dueBefore: z.string().transform(Number).pipe(z.number().int().min(0)).optional(),
  dueAfter: z.string().transform(Number).pipe(z.number().int().min(0)).optional(),
  priority: z.enum(['none', 'low', 'medium', 'high', 'critical']).optional(),
  sortBy: z.enum(['dueDate', 'priority', 'createdAt', 'title']).optional(),
  sortOrder: z.enum(['asc', 'desc']).optional(),
  limit: z.string().transform(Number).pipe(z.number().int().min(1).max(100)).optional(),
  offset: z.string().transform(Number).pipe(z.number().int().min(0)).optional(),
});