const { User, UserAddress } = require("../models");
const emailServices = require("./email");
const logger = require("../utils/logger");

/**
 * UserService handles user-related business logic
 * Including profile updates and associated notifications
 */
class UserService {
  /**
   * Update user profile and send notification if personal info changed
   * @param {string} userId - User ID
   * @param {Object} rawUpdateData - Data to update
   * @param {Object} options - Optional transaction or other options
   * @returns {Promise<User>} Updated user (without password field)
   */
  async updateProfile(userId, rawUpdateData, options = {}) {
    const user = await User.findByPk(userId);
    if (!user) {
      throw new Error("User not found");
    }

    // Store original values for comparison
    const originalValues = {
      email: user.email,
      firstName: user.firstName,
      lastName: user.lastName,
      address1: user.address1,
      address2: user.address2,
      city: user.city,
      state: user.state,
      zipCode: user.zipCode,
      country: user.country,
    };

    // Prepare update data with preprocessing
    const updateData = { ...rawUpdateData };

    // Only include email if it's not empty
    if (updateData.email !== undefined) {
      if (updateData.email && updateData.email.trim() !== "") {
        updateData.email = updateData.email.trim();
      } else {
        delete updateData.email; // Don't update if empty
      }
    }

    // Handle phone: convert empty strings to null to avoid unique constraint issues
    if (updateData.phone !== undefined) {
      updateData.phone =
        updateData.phone && updateData.phone.trim() !== ""
          ? updateData.phone.trim()
          : null;
    }

    // Perform the update
    await user.update(updateData, options);

    // Check if personal information changed
    const personalInfoFields = [
      "email",
      "firstName",
      "lastName",
      "address1",
      "address2",
      "city",
      "state",
      "zipCode",
      "country",
    ];

    const changedFields = personalInfoFields.filter(
      (field) =>
        updateData[field] !== undefined &&
        originalValues[field] !== updateData[field]
    );

    // Send notification email if personal info changed
    if (changedFields.length > 0 && process.env.NODE_ENV !== "test") {
      try {
        await emailServices.auth.sendPersonalInfoChangedEmail(user);
        logger.info("Personal information changed notification sent", {
          userId: user.id,
          email: user.email,
          changedFields,
        });
      } catch (emailError) {
        logger.error(
          "Failed to send personal information changed notification",
          {
            error: emailError.message,
            stack: emailError.stack,
            userId: user.id,
            email: user.email,
            changedFields,
          }
        );
        // Don't throw - email failure shouldn't fail the update
      }
    }

    // Return user without password
    const updatedUser = await User.findByPk(user.id, {
      attributes: { exclude: ["password"] },
    });

    return updatedUser;
  }

  /**
   * Create a new address for a user and send notification
   * @param {string} userId - User ID
   * @param {Object} addressData - Address data
   * @returns {Promise<UserAddress>} Created address
   */
  async createUserAddress(userId, addressData) {
    const user = await User.findByPk(userId);
    if (!user) {
      throw new Error("User not found");
    }

    const address = await UserAddress.create({
      ...addressData,
      userId,
    });

    // Send notification for address creation
    if (process.env.NODE_ENV !== "test") {
      try {
        await emailServices.auth.sendPersonalInfoChangedEmail(user);
        logger.info(
          "Personal information changed notification sent (address created)",
          {
            userId: user.id,
            email: user.email,
            addressId: address.id,
          }
        );
      } catch (emailError) {
        logger.error("Failed to send notification for address creation", {
          error: emailError.message,
          stack: emailError.stack,
          userId: user.id,
          addressId: address.id,
        });
      }
    }

    return address;
  }

  /**
   * Update a user address and send notification
   * @param {string} userId - User ID
   * @param {string} addressId - Address ID
   * @param {Object} updateData - Data to update
   * @returns {Promise<UserAddress>} Updated address
   */
  async updateUserAddress(userId, addressId, updateData) {
    const address = await UserAddress.findOne({
      where: { id: addressId, userId },
    });

    if (!address) {
      throw new Error("Address not found");
    }

    await address.update(updateData);

    // Send notification for address update
    if (process.env.NODE_ENV !== "test") {
      try {
        const user = await User.findByPk(userId);
        await emailServices.auth.sendPersonalInfoChangedEmail(user);
        logger.info(
          "Personal information changed notification sent (address updated)",
          {
            userId: user.id,
            email: user.email,
            addressId: address.id,
          }
        );
      } catch (emailError) {
        logger.error("Failed to send notification for address update", {
          error: emailError.message,
          stack: emailError.stack,
          userId,
          addressId: address.id,
        });
      }
    }

    return address;
  }

  /**
   * Delete a user address and send notification
   * @param {string} userId - User ID
   * @param {string} addressId - Address ID
   * @returns {Promise<void>}
   */
  async deleteUserAddress(userId, addressId) {
    const address = await UserAddress.findOne({
      where: { id: addressId, userId },
    });

    if (!address) {
      throw new Error("Address not found");
    }

    await address.destroy();

    // Send notification for address deletion
    if (process.env.NODE_ENV !== "test") {
      try {
        const user = await User.findByPk(userId);
        await emailServices.auth.sendPersonalInfoChangedEmail(user);
        logger.info(
          "Personal information changed notification sent (address deleted)",
          {
            userId: user.id,
            email: user.email,
            addressId,
          }
        );
      } catch (emailError) {
        logger.error("Failed to send notification for address deletion", {
          error: emailError.message,
          stack: emailError.stack,
          userId,
          addressId,
        });
      }
    }
  }
}

module.exports = new UserService();
