import * as crypto from 'node:crypto';
import type { FastifyInstance, FastifyPluginAsync, FastifyRequest, FastifyReply } from 'fastify';
import { sendEmail } from '../utils/email';

interface UpdateProfileBody {
  name: string;
}

interface ChangePasswordBody {
  currentPassword: string;
  newPassword: string;
}

interface ChangeEmailBody {
  newEmail: string;
  password: string;
}

interface ForgotPasswordBody {
  email: string;
}

interface ResetPasswordBody {
  token: string;
  newPassword: string;
}

interface DeleteAccountBody {
  password?: string;
}

interface UpdatePreferencesBody {
  dietaryPreference?: string | null;
  allergies?: string | null;
  maxCookingTime?: number | null;
  equipment?: string[] | null;
  cuisinePreference?: string | null;
  servingsDefault?: number | null;
}

const usersRoutes: FastifyPluginAsync = async (fastify: FastifyInstance) => {
  const authenticateUser = async (request: FastifyRequest, reply: FastifyReply) => {
    try {
      await fastify.authenticate(request, reply);
    } catch {
      reply.code(401).send({ error: 'Authentification requise' });
    }
  };

  // --- GET /profile ---
  fastify.get('/profile', { preHandler: authenticateUser }, async (request, reply) => {
    try {
      const user = await fastify.prisma.user.findUnique({
        where: { id: request.user.id },
        select: {
          id: true,
          email: true,
          name: true,
          subscription: true,
          createdAt: true,
          dietaryPreference: true,
          allergies: true,
          maxCookingTime: true,
          equipment: true,
          cuisinePreference: true,
          servingsDefault: true,
        },
      });

      if (!user) {
        return reply.code(404).send({ error: 'Utilisateur non trouvé' });
      }

      // Désérialise le JSON equipment avant de renvoyer au client
      let equipmentArr: string[] = [];
      if (user.equipment) {
        try {
          const parsed = JSON.parse(user.equipment);
          if (Array.isArray(parsed)) equipmentArr = parsed;
        } catch {
          /* ignore */
        }
      }

      return {
        user: {
          ...user,
          equipment: equipmentArr,
        },
      };
    } catch (error) {
      fastify.log.error(error);
      return reply.code(500).send({ error: 'Erreur lors de la récupération du profil' });
    }
  });

  // --- PUT /profile ---
  fastify.put<{ Body: UpdateProfileBody }>(
    '/profile',
    { preHandler: authenticateUser },
    async (request, reply) => {
      try {
        const { name } = request.body;
        if (!name) {
          return reply.code(400).send({ error: 'Le nom est requis' });
        }

        const updatedUser = await fastify.prisma.user.update({
          where: { id: request.user.id },
          data: { name },
          select: { id: true, email: true, name: true, subscription: true },
        });

        return { user: updatedUser };
      } catch (error) {
        fastify.log.error(error);
        return reply.code(500).send({ error: 'Erreur lors de la mise à jour du profil' });
      }
    }
  );

  // --- PUT /change-password ---
  fastify.put<{ Body: ChangePasswordBody }>(
    '/change-password',
    { preHandler: authenticateUser },
    async (request, reply) => {
      try {
        const { currentPassword, newPassword } = request.body;

        if (!currentPassword || !newPassword) {
          return reply
            .code(400)
            .send({ error: 'Les mots de passe actuels et nouveaux sont requis' });
        }

        if (newPassword.length < 8) {
          return reply
            .code(400)
            .send({ error: 'Le nouveau mot de passe doit contenir au moins 8 caractères' });
        }

        const user = await fastify.prisma.user.findUnique({
          where: { id: request.user.id },
        });

        if (!user || !user.password) {
          return reply
            .code(400)
            .send({ error: 'Utilisateur non trouvé ou connecté via Google' });
        }

        const isPasswordValid = await fastify.comparePassword(currentPassword, user.password);
        if (!isPasswordValid) {
          return reply.code(400).send({ error: 'Mot de passe actuel incorrect' });
        }

        const hashedPassword = await fastify.hashPassword(newPassword);
        await fastify.prisma.user.update({
          where: { id: request.user.id },
          data: { password: hashedPassword },
        });

        return { success: true, message: 'Mot de passe mis à jour avec succès' };
      } catch (error) {
        fastify.log.error(error);
        return reply.code(500).send({ error: 'Erreur lors du changement de mot de passe' });
      }
    }
  );

  // --- PUT /change-email ---
  fastify.put<{ Body: ChangeEmailBody }>(
    '/change-email',
    { preHandler: authenticateUser },
    async (request, reply) => {
      try {
        const { newEmail, password } = request.body;

        if (!newEmail || !password) {
          return reply
            .code(400)
            .send({ error: 'Le nouvel email et le mot de passe sont requis' });
        }

        const existingUser = await fastify.prisma.user.findUnique({ where: { email: newEmail } });
        if (existingUser) {
          return reply.code(400).send({ error: 'Cet email est déjà utilisé' });
        }

        const user = await fastify.prisma.user.findUnique({ where: { id: request.user.id } });
        if (!user || !user.password) {
          return reply
            .code(400)
            .send({ error: 'Utilisateur non trouvé ou connecté via Google' });
        }

        const isPasswordValid = await fastify.comparePassword(password, user.password);
        if (!isPasswordValid) {
          return reply.code(400).send({ error: 'Mot de passe incorrect' });
        }

        await fastify.prisma.user.update({
          where: { id: request.user.id },
          data: { email: newEmail },
        });

        return { success: true, message: 'Email mis à jour avec succès' };
      } catch (error) {
        fastify.log.error(error);
        return reply.code(500).send({ error: "Erreur lors du changement d'email" });
      }
    }
  );

  // --- POST /forgot-password ---
  fastify.post<{ Body: ForgotPasswordBody }>('/forgot-password', async (request, reply) => {
    try {
      const { email } = request.body;

      if (!email) {
        return reply.code(400).send({ error: 'Email requis' });
      }

      const user = await fastify.prisma.user.findUnique({ where: { email } });

      // Réponse générique pour éviter l'énumération d'utilisateurs
      const genericResponse = {
        success: true,
        message: 'Si un compte existe avec cet email, un lien de réinitialisation a été envoyé',
      };

      if (!user) {
        return reply.code(200).send(genericResponse);
      }

      const resetToken = crypto.randomBytes(32).toString('hex');
      const resetTokenExpiry = new Date(Date.now() + 3600000); // 1h

      await fastify.prisma.user.update({
        where: { id: user.id },
        data: { resetToken, resetTokenExpiry },
      });

      const resetUrl = `${
        process.env.FRONTEND_URL || 'http://localhost:5173'
      }/reset-password?token=${resetToken}`;

      try {
        await sendEmail({
          to: user.email,
          subject: 'Réinitialisation de votre mot de passe',
          text: `Pour réinitialiser votre mot de passe, cliquez sur ce lien: ${resetUrl}`,
          html: `
            <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #e0e0e0; border-radius: 5px;">
              <h2 style="color: #f97316;">Réinitialisation de votre mot de passe</h2>
              <p>Bonjour,</p>
              <p>Vous avez demandé la réinitialisation de votre mot de passe. Cliquez sur le bouton ci-dessous pour créer un nouveau mot de passe :</p>
              <div style="text-align: center; margin: 30px 0;">
                <a href="${resetUrl}" style="background-color: #f97316; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold;">Réinitialiser mon mot de passe</a>
              </div>
              <p>Si vous n'avez pas demandé cette réinitialisation, vous pouvez ignorer cet email.</p>
              <p>Ce lien expirera dans 1 heure.</p>
              <p>Cordialement,<br>L'équipe Freedge</p>
            </div>
          `,
        });
        fastify.log.info(`Email de réinitialisation envoyé à ${user.email}`);
      } catch (emailError) {
        fastify.log.error(`Erreur d'envoi d'email: ${(emailError as Error).message}`);
        if (process.env.NODE_ENV === 'development') {
          fastify.log.info(`Lien de réinitialisation (DEV ONLY): ${resetUrl}`);
        }
      }

      return genericResponse;
    } catch (error) {
      fastify.log.error(error);
      return reply.code(500).send({
        error: 'Erreur lors de la demande de réinitialisation de mot de passe',
      });
    }
  });

  // --- POST /reset-password ---
  fastify.post<{ Body: ResetPasswordBody }>('/reset-password', async (request, reply) => {
    try {
      const { token, newPassword } = request.body;

      if (!token || !newPassword) {
        return reply.code(400).send({ error: 'Token et nouveau mot de passe requis' });
      }

      if (newPassword.length < 8) {
        return reply
          .code(400)
          .send({ error: 'Le nouveau mot de passe doit contenir au moins 8 caractères' });
      }

      const user = await fastify.prisma.user.findFirst({
        where: {
          resetToken: token,
          resetTokenExpiry: { gt: new Date() },
        },
      });

      if (!user) {
        return reply.code(400).send({ error: 'Token invalide ou expiré' });
      }

      const hashedPassword = await fastify.hashPassword(newPassword);

      await fastify.prisma.user.update({
        where: { id: user.id },
        data: {
          password: hashedPassword,
          resetToken: null,
          resetTokenExpiry: null,
        },
      });

      return { success: true, message: 'Mot de passe réinitialisé avec succès' };
    } catch (error) {
      fastify.log.error(error);
      return reply.code(500).send({ error: 'Erreur lors de la réinitialisation du mot de passe' });
    }
  });

  // --- DELETE /account ---
  fastify.delete<{ Body: DeleteAccountBody }>(
    '/account',
    { preHandler: authenticateUser },
    async (request, reply) => {
      try {
        const { password } = request.body ?? {};

        const user = await fastify.prisma.user.findUnique({ where: { id: request.user.id } });

        if (!user) {
          return reply.code(404).send({ error: 'Utilisateur non trouvé' });
        }

        if (user.password) {
          if (!password) {
            return reply.code(400).send({ error: 'Mot de passe requis' });
          }
          const isPasswordValid = await fastify.comparePassword(password, user.password);
          if (!isPasswordValid) {
            return reply.code(400).send({ error: 'Mot de passe incorrect' });
          }
        }

        await fastify.prisma.recipe.deleteMany({ where: { userId: user.id } });
        await fastify.prisma.user.delete({ where: { id: user.id } });

        return { success: true, message: 'Compte supprimé avec succès' };
      } catch (error) {
        fastify.log.error(error);
        return reply.code(500).send({ error: 'Erreur lors de la suppression du compte' });
      }
    }
  );

  // --- PUT /preferences ---
  fastify.put<{ Body: UpdatePreferencesBody }>(
    '/preferences',
    { preHandler: authenticateUser },
    async (request, reply) => {
      try {
        const body = request.body ?? {};

        // Validation simple : on accepte null pour "reset"
        const allowedDiets = ['none', 'vegetarian', 'vegan', 'pescatarian', null];
        if (body.dietaryPreference !== undefined && !allowedDiets.includes(body.dietaryPreference)) {
          return reply.code(400).send({ error: 'Régime invalide' });
        }

        if (
          body.maxCookingTime !== undefined &&
          body.maxCookingTime !== null &&
          (body.maxCookingTime < 5 || body.maxCookingTime > 600)
        ) {
          return reply.code(400).send({ error: 'Temps de cuisson invalide (5-600 min)' });
        }

        if (
          body.servingsDefault !== undefined &&
          body.servingsDefault !== null &&
          (body.servingsDefault < 1 || body.servingsDefault > 20)
        ) {
          return reply.code(400).send({ error: 'Nombre de portions invalide (1-20)' });
        }

        const updateData: Record<string, unknown> = {};
        if (body.dietaryPreference !== undefined) updateData.dietaryPreference = body.dietaryPreference;
        if (body.allergies !== undefined) updateData.allergies = body.allergies;
        if (body.maxCookingTime !== undefined) updateData.maxCookingTime = body.maxCookingTime;
        if (body.cuisinePreference !== undefined) updateData.cuisinePreference = body.cuisinePreference;
        if (body.servingsDefault !== undefined) updateData.servingsDefault = body.servingsDefault;
        if (body.equipment !== undefined) {
          updateData.equipment = body.equipment ? JSON.stringify(body.equipment) : null;
        }

        const updated = await fastify.prisma.user.update({
          where: { id: request.user.id },
          data: updateData,
          select: {
            dietaryPreference: true,
            allergies: true,
            maxCookingTime: true,
            equipment: true,
            cuisinePreference: true,
            servingsDefault: true,
          },
        });

        let equipmentArr: string[] = [];
        if (updated.equipment) {
          try {
            const parsed = JSON.parse(updated.equipment);
            if (Array.isArray(parsed)) equipmentArr = parsed;
          } catch {
            /* ignore */
          }
        }

        return {
          preferences: {
            ...updated,
            equipment: equipmentArr,
          },
        };
      } catch (error) {
        fastify.log.error(error);
        return reply.code(500).send({ error: 'Erreur lors de la mise à jour des préférences' });
      }
    }
  );
};

export default usersRoutes;
