import { z } from 'zod';

// ==========================================
// Common Schemas
// ==========================================

export const emailSchema = z.string().email('Invalid email address');

export const phoneSchema = z.string().regex(
  /^[+]?[(]?[0-9]{1,4}[)]?[-\s./0-9]*$/,
  'Invalid phone number'
).optional();

export const passwordSchema = z
  .string()
  .min(8, 'Password must be at least 8 characters')
  .regex(/[A-Z]/, 'Password must contain at least one uppercase letter')
  .regex(/[a-z]/, 'Password must contain at least one lowercase letter')
  .regex(/[0-9]/, 'Password must contain at least one number');

export const timeSchema = z.string().regex(
  /^([01]?[0-9]|2[0-3]):[0-5][0-9]$/,
  'Invalid time format (HH:mm)'
);

export const slugSchema = z
  .string()
  .min(2, 'Slug must be at least 2 characters')
  .max(50, 'Slug must be at most 50 characters')
  .regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, 'Slug must be lowercase and contain only letters, numbers, and hyphens');

// ==========================================
// Auth Schemas
// ==========================================

export const loginSchema = z.object({
  email: emailSchema,
  password: z.string().min(1, 'Password is required'),
  rememberMe: z.boolean().optional().default(false),
});

export type LoginInput = z.infer<typeof loginSchema>;

export const registerClientSchema = z.object({
  email: emailSchema,
  password: passwordSchema,
  confirmPassword: z.string(),
  firstName: z.string().min(1, 'First name is required').max(50),
  lastName: z.string().min(1, 'Last name is required').max(50),
  phone: phoneSchema,
  dateOfBirth: z.coerce.date().optional(),
  gender: z.enum(['male', 'female', 'other', 'prefer_not_to_say']).optional(),
  marketingConsent: z.boolean().optional().default(false),
  termsAccepted: z.boolean().refine(val => val === true, {
    message: 'You must accept the terms and conditions',
  }),
}).refine(data => data.password === data.confirmPassword, {
  message: 'Passwords do not match',
  path: ['confirmPassword'],
});

export type RegisterClientInput = z.infer<typeof registerClientSchema>;

export const forgotPasswordSchema = z.object({
  email: emailSchema,
});

export type ForgotPasswordInput = z.infer<typeof forgotPasswordSchema>;

export const resetPasswordSchema = z.object({
  token: z.string().min(1, 'Reset token is required'),
  password: passwordSchema,
  confirmPassword: z.string(),
}).refine(data => data.password === data.confirmPassword, {
  message: 'Passwords do not match',
  path: ['confirmPassword'],
});

export type ResetPasswordInput = z.infer<typeof resetPasswordSchema>;

// ==========================================
// Booking Schemas
// ==========================================

export const createBookingSchema = z.object({
  courtId: z.string().uuid('Invalid court ID'),
  clientId: z.string().uuid('Invalid client ID'),
  startTime: z.coerce.date(),
  endTime: z.coerce.date(),
  participants: z.array(z.object({
    clientId: z.string().uuid().optional(),
    name: z.string().min(1, 'Participant name is required'),
    email: emailSchema.optional(),
    phone: phoneSchema,
    isPayer: z.boolean().default(false),
  })).optional().default([]),
  paymentType: z.enum(['cash', 'card', 'transfer', 'membership', 'pending']).default('pending'),
  notes: z.string().max(500).optional(),
}).refine(data => data.endTime > data.startTime, {
  message: 'End time must be after start time',
  path: ['endTime'],
});

export type CreateBookingInput = z.infer<typeof createBookingSchema>;

export const updateBookingStatusSchema = z.object({
  bookingId: z.string().uuid('Invalid booking ID'),
  status: z.enum(['pending', 'confirmed', 'cancelled', 'completed', 'no_show']),
  cancellationReason: z.string().max(500).optional(),
});

export type UpdateBookingStatusInput = z.infer<typeof updateBookingStatusSchema>;

export const createRecurringBookingSchema = z.object({
  courtId: z.string().uuid('Invalid court ID'),
  clientId: z.string().uuid('Invalid client ID'),
  dayOfWeek: z.number().int().min(0).max(6),
  startTime: timeSchema,
  endTime: timeSchema,
  startDate: z.coerce.date(),
  endDate: z.coerce.date().optional(),
  paymentType: z.enum(['cash', 'card', 'transfer', 'membership', 'pending']).default('pending'),
  notes: z.string().max(500).optional(),
}).refine(data => {
  const [startH, startM] = data.startTime.split(':').map(Number);
  const [endH, endM] = data.endTime.split(':').map(Number);
  return endH * 60 + endM > startH * 60 + startM;
}, {
  message: 'End time must be after start time',
  path: ['endTime'],
});

export type CreateRecurringBookingInput = z.infer<typeof createRecurringBookingSchema>;

// ==========================================
// Site & Court Schemas
// ==========================================

const dayScheduleSchema = z.object({
  isOpen: z.boolean(),
  openTime: timeSchema.optional(),
  closeTime: timeSchema.optional(),
  breaks: z.array(z.object({
    startTime: timeSchema,
    endTime: timeSchema,
  })).optional(),
}).refine(data => {
  if (data.isOpen) {
    return data.openTime !== undefined && data.closeTime !== undefined;
  }
  return true;
}, {
  message: 'Open and close times are required when the day is open',
});

const weeklyScheduleSchema = z.object({
  monday: dayScheduleSchema,
  tuesday: dayScheduleSchema,
  wednesday: dayScheduleSchema,
  thursday: dayScheduleSchema,
  friday: dayScheduleSchema,
  saturday: dayScheduleSchema,
  sunday: dayScheduleSchema,
});

export const createSiteSchema = z.object({
  name: z.string().min(1, 'Site name is required').max(100),
  slug: slugSchema,
  address: z.string().min(1, 'Address is required').max(200),
  city: z.string().min(1, 'City is required').max(100),
  state: z.string().max(100).optional(),
  country: z.string().min(1, 'Country is required').max(100),
  postalCode: z.string().max(20).optional(),
  latitude: z.number().min(-90).max(90).optional(),
  longitude: z.number().min(-180).max(180).optional(),
  phone: phoneSchema,
  email: emailSchema.optional(),
  openingHours: weeklyScheduleSchema,
  amenities: z.array(z.string()).optional().default([]),
  images: z.array(z.string().url()).optional().default([]),
  isActive: z.boolean().optional().default(true),
});

export type CreateSiteInput = z.infer<typeof createSiteSchema>;

export const updateSiteSchema = createSiteSchema.partial();

export type UpdateSiteInput = z.infer<typeof updateSiteSchema>;

export const createCourtSchema = z.object({
  siteId: z.string().uuid('Invalid site ID'),
  name: z.string().min(1, 'Court name is required').max(50),
  type: z.enum(['indoor', 'outdoor', 'covered']),
  status: z.enum(['active', 'maintenance', 'inactive']).default('active'),
  description: z.string().max(500).optional(),
  features: z.array(z.string()).optional().default([]),
  hourlyRate: z.number().positive('Hourly rate must be positive'),
  peakHourlyRate: z.number().positive().optional(),
  peakHours: z.array(z.object({
    startTime: timeSchema,
    endTime: timeSchema,
  })).optional(),
  images: z.array(z.string().url()).optional().default([]),
  position: z.number().int().min(0).optional(),
});

export type CreateCourtInput = z.infer<typeof createCourtSchema>;

export const updateCourtSchema = createCourtSchema.partial().omit({ siteId: true });

export type UpdateCourtInput = z.infer<typeof updateCourtSchema>;

// ==========================================
// Product & Sales Schemas
// ==========================================

export const createProductCategorySchema = z.object({
  name: z.string().min(1, 'Category name is required').max(100),
  description: z.string().max(500).optional(),
  parentId: z.string().uuid().optional(),
  position: z.number().int().min(0).optional().default(0),
  isActive: z.boolean().optional().default(true),
});

export type CreateProductCategoryInput = z.infer<typeof createProductCategorySchema>;

export const createProductSchema = z.object({
  categoryId: z.string().uuid().optional(),
  siteIds: z.array(z.string().uuid()).min(1, 'At least one site is required'),
  name: z.string().min(1, 'Product name is required').max(100),
  description: z.string().max(1000).optional(),
  sku: z.string().max(50).optional(),
  barcode: z.string().max(50).optional(),
  price: z.number().nonnegative('Price must be non-negative'),
  cost: z.number().nonnegative().optional(),
  currency: z.string().length(3, 'Currency must be a 3-letter code').default('EUR'),
  taxRate: z.number().min(0).max(100).default(21),
  trackInventory: z.boolean().optional().default(false),
  images: z.array(z.string().url()).optional().default([]),
  isActive: z.boolean().optional().default(true),
});

export type CreateProductInput = z.infer<typeof createProductSchema>;

export const updateProductSchema = createProductSchema.partial();

export type UpdateProductInput = z.infer<typeof updateProductSchema>;

const saleItemSchema = z.object({
  productId: z.string().uuid().optional(),
  bookingId: z.string().uuid().optional(),
  membershipId: z.string().uuid().optional(),
  description: z.string().min(1, 'Item description is required').max(200),
  quantity: z.number().int().positive('Quantity must be positive'),
  unitPrice: z.number().nonnegative('Unit price must be non-negative'),
  taxRate: z.number().min(0).max(100).default(21),
  discountAmount: z.number().nonnegative().optional().default(0),
});

export const createSaleSchema = z.object({
  siteId: z.string().uuid('Invalid site ID'),
  clientId: z.string().uuid().optional(),
  cashRegisterId: z.string().uuid().optional(),
  items: z.array(saleItemSchema).min(1, 'At least one item is required'),
  discountAmount: z.number().nonnegative().optional().default(0),
  paymentType: z.enum(['cash', 'card', 'transfer', 'membership', 'pending']),
  paymentReference: z.string().max(100).optional(),
  notes: z.string().max(500).optional(),
});

export type CreateSaleInput = z.infer<typeof createSaleSchema>;

// ==========================================
// Tournament Schemas
// ==========================================

const tournamentCategorySchema = z.object({
  id: z.string().uuid().optional(),
  name: z.string().min(1, 'Category name is required').max(50),
  description: z.string().max(200).optional(),
  minSkillLevel: z.enum(['beginner', 'intermediate', 'advanced', 'professional']).optional(),
  maxSkillLevel: z.enum(['beginner', 'intermediate', 'advanced', 'professional']).optional(),
  minAge: z.number().int().min(0).max(100).optional(),
  maxAge: z.number().int().min(0).max(100).optional(),
  gender: z.enum(['male', 'female', 'mixed', 'any']).optional(),
});

const tournamentPrizeSchema = z.object({
  position: z.number().int().positive(),
  description: z.string().min(1).max(200),
  amount: z.number().nonnegative().optional(),
  currency: z.string().length(3).optional(),
});

export const createTournamentSchema = z.object({
  siteId: z.string().uuid('Invalid site ID'),
  name: z.string().min(1, 'Tournament name is required').max(100),
  description: z.string().max(2000).optional(),
  type: z.enum(['single_elimination', 'double_elimination', 'round_robin', 'swiss']),
  startDate: z.coerce.date(),
  endDate: z.coerce.date(),
  registrationStartDate: z.coerce.date(),
  registrationEndDate: z.coerce.date(),
  maxTeams: z.number().int().min(2, 'Minimum 2 teams required').max(256),
  minTeams: z.number().int().min(2, 'Minimum 2 teams required').default(2),
  teamSize: z.number().int().min(1).max(4).default(2),
  entryFee: z.number().nonnegative().default(0),
  currency: z.string().length(3, 'Currency must be a 3-letter code').default('EUR'),
  prizePool: z.number().nonnegative().optional(),
  prizes: z.array(tournamentPrizeSchema).optional(),
  rules: z.string().max(5000).optional(),
  courtIds: z.array(z.string().uuid()).min(1, 'At least one court is required'),
  categories: z.array(tournamentCategorySchema).min(1, 'At least one category is required'),
  images: z.array(z.string().url()).optional().default([]),
  isPublic: z.boolean().optional().default(true),
}).refine(data => data.endDate >= data.startDate, {
  message: 'End date must be on or after start date',
  path: ['endDate'],
}).refine(data => data.registrationEndDate >= data.registrationStartDate, {
  message: 'Registration end date must be on or after registration start date',
  path: ['registrationEndDate'],
}).refine(data => data.registrationEndDate <= data.startDate, {
  message: 'Registration must end before or on tournament start date',
  path: ['registrationEndDate'],
}).refine(data => data.minTeams <= data.maxTeams, {
  message: 'Minimum teams cannot exceed maximum teams',
  path: ['minTeams'],
});

export type CreateTournamentInput = z.infer<typeof createTournamentSchema>;

// Base tournament schema without refinements for partial updates
const baseTournamentSchema = z.object({
  name: z.string().min(1, 'Tournament name is required').max(100),
  description: z.string().max(2000).optional(),
  type: z.enum(['single_elimination', 'double_elimination', 'round_robin', 'swiss']),
  startDate: z.coerce.date(),
  endDate: z.coerce.date(),
  registrationStartDate: z.coerce.date(),
  registrationEndDate: z.coerce.date(),
  maxTeams: z.number().int().min(2, 'Minimum 2 teams required').max(256),
  minTeams: z.number().int().min(2, 'Minimum 2 teams required').default(2),
  teamSize: z.number().int().min(1).max(4).default(2),
  entryFee: z.number().nonnegative().default(0),
  currency: z.string().length(3, 'Currency must be a 3-letter code').default('EUR'),
  prizePool: z.number().nonnegative().optional(),
  prizes: z.array(tournamentPrizeSchema).optional(),
  rules: z.string().max(5000).optional(),
  courtIds: z.array(z.string().uuid()).min(1, 'At least one court is required'),
  categories: z.array(tournamentCategorySchema).min(1, 'At least one category is required'),
  images: z.array(z.string().url()).optional().default([]),
  isPublic: z.boolean().optional().default(true),
  status: z.enum(['draft', 'registration_open', 'registration_closed', 'in_progress', 'completed', 'cancelled']).optional(),
});

export const updateTournamentSchema = baseTournamentSchema.partial();

export type UpdateTournamentInput = z.infer<typeof updateTournamentSchema>;

export const registerTournamentTeamSchema = z.object({
  tournamentId: z.string().uuid('Invalid tournament ID'),
  categoryId: z.string().uuid('Invalid category ID'),
  name: z.string().min(1, 'Team name is required').max(50),
  players: z.array(z.object({
    clientId: z.string().uuid('Invalid client ID'),
    firstName: z.string().min(1).max(50),
    lastName: z.string().min(1).max(50),
    email: emailSchema.optional(),
    phone: phoneSchema,
    isCaptain: z.boolean().default(false),
  })).min(1, 'At least one player is required'),
});

export type RegisterTournamentTeamInput = z.infer<typeof registerTournamentTeamSchema>;

export const updateMatchScoreSchema = z.object({
  matchId: z.string().uuid('Invalid match ID'),
  score: z.object({
    sets: z.array(z.object({
      team1Score: z.number().int().min(0),
      team2Score: z.number().int().min(0),
      tiebreak: z.object({
        team1Score: z.number().int().min(0),
        team2Score: z.number().int().min(0),
      }).optional(),
    })).min(1, 'At least one set is required'),
    winner: z.enum(['team1', 'team2']),
    duration: z.number().int().positive().optional(),
  }),
});

export type UpdateMatchScoreInput = z.infer<typeof updateMatchScoreSchema>;

// ==========================================
// Membership Schemas
// ==========================================

const membershipBenefitSchema = z.object({
  type: z.enum(['discount', 'free_hours', 'priority', 'guest_pass', 'product_discount', 'custom']),
  description: z.string().min(1, 'Benefit description is required').max(200),
  value: z.number().optional(),
  conditions: z.string().max(500).optional(),
});

export const createMembershipPlanSchema = z.object({
  siteIds: z.array(z.string().uuid()).min(1, 'At least one site is required'),
  name: z.string().min(1, 'Plan name is required').max(100),
  description: z.string().max(1000).optional(),
  durationMonths: z.number().int().positive('Duration must be at least 1 month'),
  price: z.number().nonnegative('Price must be non-negative'),
  currency: z.string().length(3, 'Currency must be a 3-letter code').default('EUR'),
  benefits: z.array(membershipBenefitSchema).optional().default([]),
  bookingDiscount: z.number().min(0).max(100).optional(),
  maxActiveBookings: z.number().int().positive().optional(),
  priorityBooking: z.boolean().optional().default(false),
  guestPasses: z.number().int().nonnegative().optional(),
  isActive: z.boolean().optional().default(true),
});

export type CreateMembershipPlanInput = z.infer<typeof createMembershipPlanSchema>;

export const updateMembershipPlanSchema = createMembershipPlanSchema.partial();

export type UpdateMembershipPlanInput = z.infer<typeof updateMembershipPlanSchema>;

export const createMembershipSchema = z.object({
  clientId: z.string().uuid('Invalid client ID'),
  planId: z.string().uuid('Invalid plan ID'),
  startDate: z.coerce.date(),
  autoRenew: z.boolean().optional().default(false),
  paymentMethod: z.string().max(50).optional(),
  notes: z.string().max(500).optional(),
});

export type CreateMembershipInput = z.infer<typeof createMembershipSchema>;

export const updateMembershipStatusSchema = z.object({
  membershipId: z.string().uuid('Invalid membership ID'),
  status: z.enum(['active', 'expired', 'cancelled', 'suspended']),
  reason: z.string().max(500).optional(),
});

export type UpdateMembershipStatusInput = z.infer<typeof updateMembershipStatusSchema>;

// ==========================================
// Client Schemas
// ==========================================

export const createClientSchema = z.object({
  firstName: z.string().min(1, 'First name is required').max(50),
  lastName: z.string().min(1, 'Last name is required').max(50),
  email: emailSchema,
  phone: phoneSchema,
  dateOfBirth: z.coerce.date().optional(),
  gender: z.enum(['male', 'female', 'other', 'prefer_not_to_say']).optional(),
  address: z.string().max(200).optional(),
  city: z.string().max(100).optional(),
  postalCode: z.string().max(20).optional(),
  country: z.string().max(100).optional(),
  notes: z.string().max(1000).optional(),
  tags: z.array(z.string()).optional().default([]),
  preferredSiteId: z.string().uuid().optional(),
  skillLevel: z.enum(['beginner', 'intermediate', 'advanced', 'professional']).optional(),
  emergencyContact: z.object({
    name: z.string().min(1).max(100),
    relationship: z.string().min(1).max(50),
    phone: z.string().min(1),
  }).optional(),
  marketingConsent: z.boolean().optional().default(false),
});

export type CreateClientInput = z.infer<typeof createClientSchema>;

export const updateClientSchema = createClientSchema.partial();

export type UpdateClientInput = z.infer<typeof updateClientSchema>;

// ==========================================
// Organization Schemas
// ==========================================

export const createOrganizationSchema = z.object({
  name: z.string().min(1, 'Organization name is required').max(100),
  slug: slugSchema,
  logo: z.string().url().optional(),
  primaryColor: z.string().regex(/^#[0-9A-Fa-f]{6}$/, 'Invalid color format').optional(),
  secondaryColor: z.string().regex(/^#[0-9A-Fa-f]{6}$/, 'Invalid color format').optional(),
  email: emailSchema.optional(),
  phone: phoneSchema,
  address: z.string().max(200).optional(),
  taxId: z.string().max(50).optional(),
  settings: z.object({
    timezone: z.string().default('Europe/Madrid'),
    currency: z.string().length(3).default('EUR'),
    dateFormat: z.string().default('DD/MM/YYYY'),
    timeFormat: z.enum(['12h', '24h']).default('24h'),
    defaultBookingDuration: z.number().int().positive().default(90),
    cancellationPolicy: z.object({
      allowCancellation: z.boolean().default(true),
      hoursBeforeBooking: z.number().int().nonnegative().default(24),
      refundPercentage: z.number().min(0).max(100).default(100),
    }).optional(),
    bookingRules: z.object({
      minAdvanceBooking: z.number().int().nonnegative().default(0),
      maxAdvanceBooking: z.number().int().positive().default(14),
      requirePayment: z.boolean().default(false),
    }).optional(),
  }).optional(),
});

export type CreateOrganizationInput = z.infer<typeof createOrganizationSchema>;

export const updateOrganizationSchema = createOrganizationSchema.partial().omit({ slug: true });

export type UpdateOrganizationInput = z.infer<typeof updateOrganizationSchema>;

// ==========================================
// User Schemas
// ==========================================

export const createUserSchema = z.object({
  email: emailSchema,
  password: passwordSchema,
  firstName: z.string().min(1, 'First name is required').max(50),
  lastName: z.string().min(1, 'Last name is required').max(50),
  phone: phoneSchema,
  role: z.enum(['super_admin', 'org_admin', 'site_admin', 'staff', 'coach', 'client']),
  organizationId: z.string().uuid().optional(),
  siteIds: z.array(z.string().uuid()).optional().default([]),
  permissions: z.array(z.string()).optional().default([]),
  isActive: z.boolean().optional().default(true),
});

export type CreateUserInput = z.infer<typeof createUserSchema>;

export const updateUserSchema = createUserSchema.partial().omit({ password: true });

export type UpdateUserInput = z.infer<typeof updateUserSchema>;

export const changePasswordSchema = z.object({
  currentPassword: z.string().min(1, 'Current password is required'),
  newPassword: passwordSchema,
  confirmPassword: z.string(),
}).refine(data => data.newPassword === data.confirmPassword, {
  message: 'Passwords do not match',
  path: ['confirmPassword'],
}).refine(data => data.currentPassword !== data.newPassword, {
  message: 'New password must be different from current password',
  path: ['newPassword'],
});

export type ChangePasswordInput = z.infer<typeof changePasswordSchema>;

// ==========================================
// Pagination & Filter Schemas
// ==========================================

export const paginationSchema = z.object({
  page: z.coerce.number().int().positive().optional().default(1),
  limit: z.coerce.number().int().positive().max(100).optional().default(20),
  sortBy: z.string().optional(),
  sortOrder: z.enum(['asc', 'desc']).optional().default('desc'),
});

export type PaginationInput = z.infer<typeof paginationSchema>;

export const dateRangeSchema = z.object({
  startDate: z.coerce.date(),
  endDate: z.coerce.date(),
}).refine(data => data.endDate >= data.startDate, {
  message: 'End date must be on or after start date',
  path: ['endDate'],
});

export type DateRangeInput = z.infer<typeof dateRangeSchema>;
