import prisma from './prisma';
import sha256 from 'crypto-js/sha256';

/**
 * Collection of utility functions for working with users
 */
export const UserUtils = {
  /**
   * Create a new user
   * 
   * @param {Object} data User data
   * @param {string} data.userUsername Username
   * @param {string} data.userPassword Password (will be hashed)
   * @param {string} data.userFullName Full name
   * @param {string} data.userEmail Email (optional)
   * @param {string} data.userPhone Phone (optional)
   * @param {string} data.userStatus Status (optional, defaults to "active")
   * @param {number} data.dp_id Department ID (optional)
   * @returns {Promise<Object>} Created user (without password)
   */
  async create(data) {
    // Validate required fields
    if (!data.userUsername) {
      throw new Error('Username is required');
    }
    
    if (!data.userPassword) {
      throw new Error('Password is required');
    }
    
    if (!data.userFullName) {
      throw new Error('Full name is required');
    }
    
    // Check if username already exists
    const existingUser = await prisma.user.findFirst({
      where: {
        userUsername: data.userUsername
      }
    });
    
    if (existingUser) {
      throw new Error('Username already exists');
    }
    
    // Check if department exists if provided
    if (data.dp_id) {
      const department = await prisma.department.findUnique({
        where: {
          dp_id: parseInt(data.dp_id)
        }
      });
      
      if (!department) {
        throw new Error(`Department with ID ${data.dp_id} not found`);
      }
    }
    
    // Create random secret key
    const secretKey = this.generateRandomKey(32);
    
    // Encrypt password with SHA256
    const hashedPassword = sha256(data.userPassword).toString();
    
    // Create user
    const user = await prisma.user.create({
      data: {
        userSecretKey: secretKey,
        userUsername: data.userUsername,
        userPassword: hashedPassword,
        userFullName: data.userFullName,
        userEmail: data.userEmail || null,
        userPhone: data.userPhone || null,
        userStatus: data.userStatus ? data.userStatus.toUpperCase() : "ACTIVE",
        dp_id: data.dp_id ? parseInt(data.dp_id) : null,
        userCreatedDate: new Date(),
        userModifiedDate: new Date()
      }
    });
    
    // Return user without password
    const { userPassword, ...userWithoutPassword } = user;
    return userWithoutPassword;
  },
  
  /**
   * Get all users with pagination and filtering
   * 
   * @param {Object} options Query options
   * @param {number} options.page Page number
   * @param {number} options.limit Items per page
   * @param {string} options.search Search term
   * @param {number} options.departmentId Filter by department ID
   * @param {string} options.status Filter by status
   * @returns {Promise<Object>} Users and pagination metadata
   */
  async getAll({ page = 1, limit = 10, search = '', departmentId = null, status = null }) {
    const skip = (page - 1) * limit;
    
    // Define filters
    const filters = {
      where: {}
    };
    
    // Filter by department if provided
    if (departmentId) {
      filters.where.dp_id = parseInt(departmentId);
    }
    
    // Filter by status if provided
    if (status) {
      filters.where.userStatus = status;
    }
    
    // Add search filter if provided
    if (search) {
      filters.where = {
        ...filters.where,
        OR: [
          { userUsername: { contains: search } },
          { userFullName: { contains: search } },
          { userEmail: { contains: search } }
        ]
      };
    }
    
    // Get users with pagination
    const [users, total] = await Promise.all([
      prisma.user.findMany({
        ...filters,
        skip,
        take: limit,
        orderBy: {
          userFullName: 'asc'
        },
        select: {
          userID: true,
          userUsername: true,
          userFullName: true,
          userEmail: true,
          userPhone: true,
          userStatus: true,
          dp_id: true,
          userCreatedDate: true,
          userModifiedDate: true,
          department: {
            select: {
              dp_id: true,
              dp_name: true,
              organization: {
                select: {
                  org_id: true,
                  org_name: true
                }
              }
            }
          },
          userrole: {
            select: {
              userRoleID: true,
              role: {
                select: {
                  roleID: true,
                  roleName: true
                }
              }
            }
          }
        }
      }),
      prisma.user.count(filters)
    ]);
    
    // Calculate pagination metadata
    const totalPages = Math.ceil(total / limit);
    
    return {
      users,
      pagination: {
        page,
        limit,
        total,
        totalPages,
        hasNextPage: page < totalPages,
        hasPrevPage: page > 1
      }
    };
  },
  
  /**
   * Get user by ID
   * 
   * @param {number} id User ID
   * @returns {Promise<Object|null>} User or null if not found
   */
  async getById(id) {
    if (isNaN(parseInt(id))) {
      throw new Error('Invalid user ID');
    }
    
    const user = await prisma.user.findUnique({
      where: {
        userID: parseInt(id)
      },
      select: {
        userID: true,
        userUsername: true,
        userFullName: true,
        userEmail: true,
        userPhone: true,
        userStatus: true,
        dp_id: true,
        userCreatedDate: true,
        userModifiedDate: true,
        department: {
          select: {
            dp_id: true,
            dp_name: true,
            organization: {
              select: {
                org_id: true,
                org_name: true
              }
            }
          }
        },
        userrole: {
          select: {
            userRoleID: true,
            role: {
              select: {
                roleID: true,
                roleName: true,
                roleDescription: true
              }
            }
          }
        }
      }
    });
    
    if (!user) {
      return null;
    }
    
    return user;
  },
  
  /**
   * Update a user
   * 
   * @param {number} id User ID
   * @param {Object} data User data to update
   * @returns {Promise<Object>} Updated user (without password)
   */
  async update(id, data) {
    if (isNaN(parseInt(id))) {
      throw new Error('Invalid user ID');
    }
    
    // Check if user exists
    const existingUser = await prisma.user.findUnique({
      where: {
        userID: parseInt(id)
      }
    });
    
    if (!existingUser) {
      throw new Error('User not found');
    }
    
    // Check if department exists if provided
    if (data.dp_id !== undefined) {
      if (data.dp_id !== null) {
        const department = await prisma.department.findUnique({
          where: {
            dp_id: parseInt(data.dp_id)
          }
        });
        
        if (!department) {
          throw new Error(`Department with ID ${data.dp_id} not found`);
        }
      }
    }
    
    // Check if username is taken by another user
    if (data.userUsername && data.userUsername !== existingUser.userUsername) {
      const usernameExists = await prisma.user.findFirst({
        where: {
          userUsername: data.userUsername,
          userID: {
            not: parseInt(id)
          }
        }
      });
      
      if (usernameExists) {
        throw new Error('Username is already taken by another user');
      }
    }
    
    // Prepare update data
    const updateData = {};
    
    // Only update fields that are provided
    if (data.userUsername !== undefined) updateData.userUsername = data.userUsername;
    if (data.userFullName !== undefined) updateData.userFullName = data.userFullName;
    if (data.userEmail !== undefined) updateData.userEmail = data.userEmail;
    if (data.userPhone !== undefined) updateData.userPhone = data.userPhone;
    if (data.userStatus !== undefined) updateData.userStatus = data.userStatus ? data.userStatus.toUpperCase() : null;
    if (data.dp_id !== undefined) updateData.dp_id = data.dp_id ? parseInt(data.dp_id) : null;
    
    // Hash password if provided
    if (data.userPassword) {
      updateData.userPassword = sha256(data.userPassword).toString();
    }
    
    // Update modified date
    updateData.userModifiedDate = new Date();
    
    // Update user
    const user = await prisma.user.update({
      where: {
        userID: parseInt(id)
      },
      data: updateData
    });
    
    // Return user without password
    const { userPassword, ...userWithoutPassword } = user;
    return userWithoutPassword;
  },
  
  /**
   * Delete a user
   * 
   * @param {number} id User ID
   * @returns {Promise<Object>} Deleted user (without password)
   */
  async delete(id) {
    if (isNaN(parseInt(id))) {
      throw new Error('Invalid user ID');
    }
    
    // Check if user exists
    const existingUser = await prisma.user.findUnique({
      where: {
        userID: parseInt(id)
      },
      include: {
        userrole: {
          select: {
            userRoleID: true
          }
        }
      }
    });
    
    if (!existingUser) {
      throw new Error('User not found');
    }
    
    // Check if user has any roles assigned
    if (existingUser.userrole && existingUser.userrole.length > 0) {
      // Delete all associated user roles first
      await prisma.userrole.deleteMany({
        where: {
          userRoleUserID: parseInt(id)
        }
      });
    }
    
    // Delete user
    const user = await prisma.user.delete({
      where: {
        userID: parseInt(id)
      }
    });
    
    // Return user without password
    const { userPassword, ...userWithoutPassword } = user;
    return userWithoutPassword;
  },
  
  /**
   * Get users by department ID
   * 
   * @param {number} departmentId Department ID
   * @returns {Promise<Array>} List of users
   */
  async getByDepartment(departmentId) {
    if (isNaN(parseInt(departmentId))) {
      throw new Error('Invalid department ID');
    }
    
    // Check if department exists
    const department = await prisma.department.findUnique({
      where: {
        dp_id: parseInt(departmentId)
      }
    });
    
    if (!department) {
      throw new Error('Department not found');
    }
    
    // Get users for this department
    return prisma.user.findMany({
      where: {
        dp_id: parseInt(departmentId)
      },
      orderBy: {
        userFullName: 'asc'
      },
      select: {
        userID: true,
        userUsername: true,
        userFullName: true,
        userEmail: true,
        userPhone: true,
        userStatus: true,
        userCreatedDate: true,
        userModifiedDate: true,
        userrole: {
          select: {
            userRoleID: true,
            role: {
              select: {
                roleID: true,
                roleName: true
              }
            }
          }
        }
      }
    });
  },
  
  /**
   * Verify user credentials
   * 
   * @param {string} username Username
   * @param {string} password Raw password
   * @returns {Promise<Object|null>} User data if credentials are valid, null otherwise
   */
  async verifyCredentials(username, password) {
    if (!username || !password) {
      return null;
    }
    
    // Get user by username
    const user = await prisma.user.findFirst({
      where: {
        userUsername: username
      }
    });
    
    if (!user) {
      return null;
    }
    
    // Check password
    const hashedPassword = sha256(password).toString();
    if (user.userPassword !== hashedPassword) {
      return null;
    }
    
    // Return user without password
    const { userPassword, ...userWithoutPassword } = user;
    return userWithoutPassword;
  },
  
  /**
   * Generate a random key
   * 
   * @param {number} length Length of the key
   * @returns {string} Random key
   */
  generateRandomKey(length) {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let result = '';
    for (let i = 0; i < length; i++) {
      result += chars.charAt(Math.floor(Math.random() * chars.length));
    }
    return result;
  }
};

export default UserUtils; 