import { DataSource } from 'typeorm';
import { UserEntity as User } from './user.entity';
import * as bcrypt from 'bcrypt';
import { Repository } from 'typeorm';
import { Role } from './role.entity';
import { UserType } from './user.enum';

const SALT_ROUNDS = 10;

export class UserService {
  private userRepository: Repository<User>;
  private roleRepository: Repository<Role>;
  private readonly dataSource: DataSource;

  constructor(dataSource: DataSource) {
    this.dataSource = dataSource;
    this.userRepository = this.dataSource.getRepository(User);
    this.roleRepository = this.dataSource.getRepository(Role);
  }

  async createUser(userData: Partial<User>): Promise<User> {
    try {
      if (userData.password) {
        userData.password = await bcrypt.hash(userData.password, SALT_ROUNDS);
      }
      const user = this.userRepository.create(userData);
      return await this.userRepository.save(user);
    } catch (error) {
      console.error('Error creating user:', error);
      throw new Error(`Failed to create user: ${error instanceof Error ? error.message : String(error)}`)
    }
  }

  async getUserById(id: number): Promise<User | null> {
    try {
      return await this.userRepository.findOne({ 
        where: { id },
        relations: ['roles', 'avatarFile']
      });
    } catch (error) {
      console.error('Error getting user:', error);
      throw new Error('Failed to get user');
    }
  }

  async getUserByUsername(username: string): Promise<User | null> {
    try {
      return await this.userRepository.findOne({
        where: { username },
        relations: ['roles', 'avatarFile']
      });
    } catch (error) {
      console.error('Error getting user:', error);
      throw new Error('Failed to get user');
    }
  }

  async getUserByPhone(phone: string): Promise<User | null> {
    try {
      return await this.userRepository.findOne({
        where: { phone: phone },
        relations: ['roles', 'avatarFile']
      });
    } catch (error) {
      console.error('Error getting user by phone:', error);
      throw new Error('Failed to get user by phone');
    }
  }

  async updateUser(id: number, updateData: Partial<User>): Promise<User | null> {
    try {
      if (updateData.password) {
        updateData.password = await bcrypt.hash(updateData.password, SALT_ROUNDS);
      }
      await this.userRepository.update(id, updateData);
      return this.getUserById(id);
    } catch (error) {
      console.error('Error updating user:', error);
      throw new Error('Failed to update user');
    }
  }

  async deleteUser(id: number): Promise<void> {
    try {
      await this.userRepository.delete(id);
    } catch (error) {
      console.error('Error deleting user:', error);
      throw new Error('Failed to delete user');
    }
  }

  async getUsersWithPagination(params: {
    page: number;
    pageSize: number;
    keyword?: string;
  }): Promise<[User[], number]> {
    try {
      const { page, pageSize, keyword } = params;
      const skip = (page - 1) * pageSize;
      
      const queryBuilder = this.userRepository
        .createQueryBuilder('user')
        .leftJoinAndSelect('user.roles', 'roles')
        .leftJoinAndSelect('user.avatarFile', 'avatarFile')
        .skip(skip)
        .take(pageSize);

      if (keyword) {
        queryBuilder.where(
          'user.username LIKE :keyword OR user.nickname LIKE :keyword OR user.phone LIKE :keyword',
          { keyword: `%${keyword}%` }
        );
      }

      return await queryBuilder.getManyAndCount();
    } catch (error) {
      console.error('Error getting users with pagination:', error);
      throw new Error('Failed to get users');
    }
  }

  async verifyPassword(user: User, password: string): Promise<boolean> {
    return password === user.password || bcrypt.compare(password, user.password)
  }

  async assignRoles(userId: number, roleIds: number[]): Promise<User | null> {
    try {
      const user = await this.getUserById(userId);
      if (!user) return null;

      const roles = await this.roleRepository.findByIds(roleIds);
      user.roles = roles;
      return await this.userRepository.save(user);
    } catch (error) {
      console.error('Error assigning roles:', error);
      throw new Error('Failed to assign roles');
    }
  }

  async getUsers(): Promise<User[]> {
    try {
      const users = await this.userRepository.find({
        relations: ['roles', 'avatarFile']
      });
      return users;
    } catch (error) {
      console.error('Error getting users:', error);
      throw new Error(`Failed to get users: ${error instanceof Error ? error.message : String(error)}`)
    }
  }


  getUserRepository(): Repository<User> {
    return this.userRepository;
  }

  async getUserByAccount(account: string): Promise<User | null> {
    try {
      return await this.userRepository.findOne({
        where: [{ username: account }, { email: account }],
        relations: ['roles', 'avatarFile']
      });
    } catch (error) {
      console.error('Error getting user by account:', error);
      throw new Error('Failed to get user by account');
    }
  }

  /**
   * 通过微信openid查找用户
   * @param openid 微信openid
   * @returns 用户信息或null
   */
  async getUserByWechatOpenid(openid: string): Promise<User | null> {
    try {
      return await this.userRepository.findOne({
        where: { wechatOpenid: openid },
        relations: ['roles', 'avatarFile']
      });
    } catch (error) {
      console.error('Error getting user by wechat openid:', error);
      throw new Error('Failed to get user by wechat openid');
    }
  }

  /**
   * 通过微信unionid查找用户
   * @param unionid 微信unionid
   * @returns 用户信息或null
   */
  async getUserByWechatUnionid(unionid: string): Promise<User | null> {
    try {
      return await this.userRepository.findOne({
        where: { wechatUnionid: unionid },
        relations: ['roles', 'avatarFile']
      });
    } catch (error) {
      console.error('Error getting user by wechat unionid:', error);
      throw new Error('Failed to get user by wechat unionid');
    }
  }

  /**
   * 设置用户为学员并设置有效期
   * @param userId 用户ID
   * @param validFrom 有效期开始时间
   * @param validTo 有效期结束时间
   * @param operatorId 操作人ID
   * @returns 更新后的用户信息
   */
  async setUserAsTrainee(userId: number, validFrom: Date, validTo: Date, operatorId: number): Promise<User | null> {
    try {
      const user = await this.getUserById(userId);
      if (!user) return null;

      const oldValues = {
        userType: user.userType,
        traineeValidFrom: user.traineeValidFrom,
        traineeValidTo: user.traineeValidTo
      };

      const updateData = {
        userType: UserType.TRAINEE,
        traineeValidFrom: validFrom,
        traineeValidTo: validTo
      };

      const updatedUser = await this.updateUser(userId, updateData);

      return updatedUser;
    } catch (error) {
      console.error('Error setting user as trainee:', error);
      throw new Error('Failed to set user as trainee');
    }
  }

  /**
   * 撤销用户学员身份
   * @param userId 用户ID
   * @param operatorId 操作人ID
   * @returns 更新后的用户信息
   */
  async removeUserTraineeStatus(userId: number, operatorId: number): Promise<User | null> {
    try {
      const user = await this.getUserById(userId);
      if (!user) return null;

      const oldValues = {
        userType: user.userType,
        traineeValidFrom: user.traineeValidFrom,
        traineeValidTo: user.traineeValidTo
      };

      const updateData = {
        userType: UserType.STUDENT,
        traineeValidFrom: null,
        traineeValidTo: null
      };

      const updatedUser = await this.updateUser(userId, updateData);
      
      return updatedUser;
    } catch (error) {
      console.error('Error removing user trainee status:', error);
      throw new Error('Failed to remove user trainee status');
    }
  }

  /**
   * 更新学员有效期
   * @param userId 用户ID
   * @param validFrom 有效期开始时间
   * @param validTo 有效期结束时间
   * @param operatorId 操作人ID
   * @returns 更新后的用户信息
   */
  async updateTraineeValidity(userId: number, validFrom: Date, validTo: Date, operatorId: number): Promise<User | null> {
    try {
      const user = await this.getUserById(userId);
      if (!user || user.userType !== 'trainee') return null;

      const oldValues = {
        traineeValidFrom: user.traineeValidFrom,
        traineeValidTo: user.traineeValidTo
      };

      const updateData = {
        traineeValidFrom: validFrom,
        traineeValidTo: validTo
      };

      const updatedUser = await this.updateUser(userId, updateData);

      return updatedUser;
    } catch (error) {
      console.error('Error updating trainee validity:', error);
      throw new Error('Failed to update trainee validity');
    }
  }

  /**
   * 生成唯一的用户名，如果用户名已存在则添加随机数字
   * @param username 基础用户名
   * @param maxAttempts 最大尝试次数，默认10次
   * @returns 唯一的用户名
   */
  async generateUniqueUsername(username: string, maxAttempts: number = 10): Promise<string> {
    let uniqueUsername = username;
    let attempt = 0;
    
    while (attempt < maxAttempts) {
      const existingUser = await this.getUserByUsername(uniqueUsername);
      
      if (!existingUser) {
        return uniqueUsername;
      }
      
      // 生成4位随机数字
      const randomSuffix = Math.floor(1000 + Math.random() * 9000);
      uniqueUsername = `${username}${randomSuffix}`;
      attempt++;
    }
    
    throw new Error(`无法生成唯一的用户名，请尝试其他用户名`);
  }

  /**
   * 检查并更新学员过期状态
   * @param userId 用户ID
   * @returns 如果状态已更新返回true，否则返回false
   */
  async checkAndUpdateTraineeStatus(userId: number): Promise<boolean> {
    try {
      const user = await this.getUserById(userId);
      if (!user || user.userType !== UserType.TRAINEE || !user.traineeValidTo) {
        return false;
      }

      const now = new Date();
      const validTo = new Date(user.traineeValidTo);
      
      if (now > validTo) {
        // 学员已过期，自动变回学生
        await this.updateUser(userId, {
          userType: UserType.STUDENT,
          traineeValidFrom: null,
          traineeValidTo: null
        });
        console.log(`用户 ${user.username} (ID: ${user.id}) 学员身份已过期，自动变回学生`);
        return true;
      }
      
      return false;
    } catch (error) {
      console.error('检查学员过期状态失败:', error);
      return false;
    }
  }
}