/**
 * Validation schemas using Zod
 * Provides strict client-side validation for all forms and user inputs
 */

import { z } from 'zod';

// Schémas de base pour les types communs
export const emailSchema = z
  .string()
  .min(1, "L'email est requis")
  .email("Format d'email invalide")
  .max(255, "L'email est trop long")
  .toLowerCase();

// T0197: Updated to match backend validation (min 8 characters)
export const passwordSchema = z
  .string()
  .min(8, 'Le mot de passe doit contenir au moins 8 caractères')
  .max(128, 'Le mot de passe est trop long')
  .regex(/[A-Z]/, 'Le mot de passe doit contenir au moins une majuscule')
  .regex(/[a-z]/, 'Le mot de passe doit contenir au moins une minuscule')
  .regex(/[0-9]/, 'Le mot de passe doit contenir au moins un chiffre')
  .regex(
    /[!@#$%^&*(),.?":{}|<>]/,
    'Le mot de passe doit contenir au moins un caractère spécial',
  )
  .refine(
    (password) => !/(.)\1{3,}/.test(password),
    'Le mot de passe ne doit pas contenir de répétition de caractères',
  )
  .refine(
    (password) => !/123456|password|qwerty/i.test(password),
    'Le mot de passe ne doit pas contenir de patterns communs',
  );

export const usernameSchema = z
  .string()
  .min(3, "Le nom d'utilisateur doit contenir au moins 3 caractères")
  .max(30, "Le nom d'utilisateur est trop long")
  .regex(
    /^[a-zA-Z0-9_-]+$/,
    "Le nom d'utilisateur ne peut contenir que des lettres, chiffres, tirets et underscores",
  )
  .refine(
    (username) => !/^(admin|root|user|test|demo)$/i.test(username),
    "Ce nom d'utilisateur est réservé",
  );

export const displayNameSchema = z
  .string()
  .min(1, "Le nom d'affichage est requis")
  .max(50, "Le nom d'affichage est trop long")
  .regex(
    /^[a-zA-ZÀ-ÿ0-9\s\-'.]+$/,
    "Le nom d'affichage contient des caractères non autorisés",
  );

// Schémas pour l'authentification
export const loginSchema = z.object({
  email: emailSchema,
  password: z.string().min(1, 'Le mot de passe est requis'),
  remember_me: z.boolean().optional(),
  captcha: z.string().optional(),
});

export const registerSchema = z
  .object({
    email: emailSchema,
    password: passwordSchema,
    confirmPassword: z.string(),
    username: usernameSchema,
    displayName: displayNameSchema,
    acceptTerms: z
      .boolean()
      .refine(
        (val) => val === true,
        "Vous devez accepter les conditions d'utilisation",
      ),
    acceptPrivacy: z
      .boolean()
      .refine(
        (val) => val === true,
        'Vous devez accepter la politique de confidentialité',
      ),
  })
  .refine((data) => data.password === data.confirmPassword, {
    message: 'Les mots de passe ne correspondent pas',
    path: ['confirmPassword'],
  });

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

export const resetPasswordSchema = z
  .object({
    token: z.string().min(1, 'Le token de réinitialisation est requis'),
    password: passwordSchema,
    confirmPassword: z.string(),
  })
  .refine((data) => data.password === data.confirmPassword, {
    message: 'Les mots de passe ne correspondent pas',
    path: ['confirmPassword'],
  });

export const changePasswordSchema = z
  .object({
    currentPassword: z.string().min(1, 'Le mot de passe actuel est requis'),
    newPassword: passwordSchema,
    confirmNewPassword: z.string(),
  })
  .refine((data) => data.newPassword === data.confirmNewPassword, {
    message: 'Les nouveaux mots de passe ne correspondent pas',
    path: ['confirmNewPassword'],
  })
  .refine((data) => data.currentPassword !== data.newPassword, {
    message: "Le nouveau mot de passe doit être différent de l'actuel",
    path: ['newPassword'],
  });

// Schémas pour les fichiers
export const fileUploadSchema = z
  .object({
    file: z.instanceof(File, { message: 'Un fichier est requis' }),
    title: z
      .string()
      .min(1, 'Le titre est requis')
      .max(255, 'Le titre est trop long'),
    artist: z
      .string()
      .min(1, "L'artiste est requis")
      .max(255, "L'artiste est trop long"),
    description: z
      .string()
      .max(1000, 'La description est trop longue')
      .optional(),
    tags: z
      .array(z.string().max(50))
      .max(10, 'Maximum 10 tags autorisés')
      .optional(),
    isPublic: z.boolean().optional(),
  })
  .refine(
    (data) => {
      const maxSize = 100 * 1024 * 1024; // 100MB
      return data.file.size <= maxSize;
    },
    {
      message: 'Le fichier est trop volumineux (maximum 100MB)',
      path: ['file'],
    },
  )
  .refine(
    (data) => {
      const allowedTypes = [
        'audio/mpeg',
        'audio/mp3',
        'audio/wav',
        'audio/flac',
        'audio/aac',
        'audio/ogg',
        'audio/m4a',
      ];
      return allowedTypes.includes(data.file.type);
    },
    {
      message: 'Type de fichier non supporté',
      path: ['file'],
    },
  );

export const imageUploadSchema = z
  .object({
    file: z.instanceof(File, { message: 'Une image est requise' }),
    alt: z.string().max(255, 'Le texte alternatif est trop long').optional(),
    caption: z.string().max(500, 'La légende est trop longue').optional(),
  })
  .refine(
    (data) => {
      const maxSize = 10 * 1024 * 1024; // 10MB
      return data.file.size <= maxSize;
    },
    {
      message: "L'image est trop volumineuse (maximum 10MB)",
      path: ['file'],
    },
  )
  .refine(
    (data) => {
      const allowedTypes = [
        'image/jpeg',
        'image/png',
        'image/gif',
        'image/webp',
        'image/svg+xml',
      ];
      return allowedTypes.includes(data.file.type);
    },
    {
      message: "Type d'image non supporté",
      path: ['file'],
    },
  );

// Schémas pour les messages de chat
export const chatMessageSchema = z.object({
  content: z
    .string()
    .min(1, 'Le message ne peut pas être vide')
    .max(2000, 'Le message est trop long (maximum 2000 caractères)')
    .refine(
      (content) => !/<script|javascript:|vbscript:|on\w+=/i.test(content),
      'Le message contient du contenu non autorisé',
    ),
  conversationId: z.string().uuid('ID de conversation invalide'),
  replyToId: z.string().uuid('ID de réponse invalide').optional(),
  attachments: z
    .array(z.string().uuid())
    .max(5, 'Maximum 5 pièces jointes')
    .optional(),
});

// Schémas pour les conversations
export const createConversationSchema = z.object({
  name: z.string().min(1, 'Le nom est requis').max(100, 'Le nom est trop long'),
  description: z.string().max(500, 'La description est trop longue').optional(),
  isPrivate: z.boolean().optional(),
  participants: z
    .array(z.string().uuid())
    .min(1, 'Au moins un participant est requis')
    .max(50, 'Maximum 50 participants'),
});

export const updateConversationSchema = z.object({
  name: z
    .string()
    .min(1, 'Le nom est requis')
    .max(100, 'Le nom est trop long')
    .optional(),
  description: z.string().max(500, 'La description est trop longue').optional(),
  isPrivate: z.boolean().optional(),
});

// Schémas pour le profil utilisateur
export const updateProfileSchema = z.object({
  displayName: displayNameSchema.optional(),
  bio: z.string().max(500, 'La bio est trop longue').optional(),
  avatar: z.string().url("URL d'avatar invalide").optional(),
  website: z.string().url('URL de site web invalide').optional(),
  location: z.string().max(100, 'La localisation est trop longue').optional(),
  birthDate: z
    .string()
    .regex(/^\d{4}-\d{2}-\d{2}$/, 'Format de date invalide')
    .optional(),
  isPublic: z.boolean().optional(),
});

// Schémas pour les paramètres
export const updateSettingsSchema = z.object({
  email: emailSchema.optional(),
  language: z
    .enum(['en', 'fr', 'es', 'de'], {
      errorMap: () => ({ message: 'Langue non supportée' }),
    })
    .optional(),
  timezone: z.string().max(50, 'Timezone invalide').optional(),
  notifications: z
    .object({
      email: z.boolean().optional(),
      push: z.boolean().optional(),
      chat: z.boolean().optional(),
      mentions: z.boolean().optional(),
    })
    .optional(),
  privacy: z
    .object({
      showOnlineStatus: z.boolean().optional(),
      showLastSeen: z.boolean().optional(),
      allowDirectMessages: z.boolean().optional(),
      showInSearch: z.boolean().optional(),
    })
    .optional(),
});

// Schémas pour la recherche
export const searchSchema = z.object({
  query: z
    .string()
    .min(1, 'La requête de recherche est requise')
    .max(100, 'La requête est trop longue'),
  type: z
    .enum(['all', 'users', 'tracks', 'conversations'], {
      errorMap: () => ({ message: 'Type de recherche invalide' }),
    })
    .optional(),
  limit: z.number().min(1).max(100).optional(),
  offset: z.number().min(0).optional(),
});

// Schémas pour la pagination
export const paginationSchema = z.object({
  page: z
    .number()
    .min(1, 'Le numéro de page doit être supérieur à 0')
    .optional(),
  limit: z
    .number()
    .min(1)
    .max(100, 'La limite doit être entre 1 et 100')
    .optional(),
  cursor: z.string().optional(),
  sortBy: z.string().max(50).optional(),
  sortOrder: z.enum(['asc', 'desc']).optional(),
});

// Schémas pour les filtres
export const trackFiltersSchema = z.object({
  genre: z.string().max(50).optional(),
  artist: z.string().max(100).optional(),
  year: z.number().min(1900).max(new Date().getFullYear()).optional(),
  duration: z
    .object({
      min: z.number().min(0).optional(),
      max: z.number().min(0).optional(),
    })
    .optional(),
  isPublic: z.boolean().optional(),
});

// Schémas pour les réponses API
export const apiResponseSchema = <T extends z.ZodType>(dataSchema: T) =>
  z.object({
    success: z.boolean(),
    data: dataSchema.optional(),
    error: z
      .object({
        code: z.number(),
        message: z.string(),
        details: z.any().optional(),
      })
      .optional(),
    meta: z
      .object({
        timestamp: z.string(),
        requestId: z.string().optional(),
        pagination: paginationSchema.optional(),
      })
      .optional(),
  });

// Schémas pour les erreurs
export const errorSchema = z.object({
  code: z.number(),
  message: z.string(),
  details: z.any().optional(),
  field: z.string().optional(),
  timestamp: z.string(),
});

// Types TypeScript générés à partir des schémas
export type LoginFormData = z.infer<typeof loginSchema>;
export type RegisterFormData = z.infer<typeof registerSchema>;
export type ForgotPasswordFormData = z.infer<typeof forgotPasswordSchema>;
export type ResetPasswordFormData = z.infer<typeof resetPasswordSchema>;
export type ChangePasswordFormData = z.infer<typeof changePasswordSchema>;
export type FileUploadFormData = z.infer<typeof fileUploadSchema>;
export type ImageUploadFormData = z.infer<typeof imageUploadSchema>;
export type ChatMessageFormData = z.infer<typeof chatMessageSchema>;
export type CreateConversationFormData = z.infer<
  typeof createConversationSchema
>;
export type UpdateConversationFormData = z.infer<
  typeof updateConversationSchema
>;
export type UpdateProfileFormData = z.infer<typeof updateProfileSchema>;
export type UpdateSettingsFormData = z.infer<typeof updateSettingsSchema>;
export type SearchFormData = z.infer<typeof searchSchema>;
export type PaginationFormData = z.infer<typeof paginationSchema>;
export type TrackFiltersFormData = z.infer<typeof trackFiltersSchema>;
export type ErrorFormData = z.infer<typeof errorSchema>;

// Utilitaires de validation
export function validateForm<T>(
  schema: z.ZodSchema<T>,
  data: unknown,
): {
  success: boolean;
  data?: T;
  errors?: Record<string, string[]>;
} {
  try {
    const result = schema.parse(data);
    return { success: true, data: result };
  } catch (error) {
    if (error instanceof z.ZodError) {
      const errors: Record<string, string[]> = {};
      error.errors.forEach((err) => {
        const path = err.path.join('.');
        if (!errors[path]) {
          errors[path] = [];
        }
        errors[path].push(err.message);
      });
      return { success: false, errors };
    }
    return {
      success: false,
      errors: { general: ['Erreur de validation inattendue'] },
    };
  }
}

// Hook React pour la validation
export function useValidation<T>(schema: z.ZodSchema<T>) {
  return (data: unknown) => validateForm(schema, data);
}
