import {
  ConflictException,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private readonly usersRepository: Repository<User>,
  ) {}

  async findAll(): Promise<User[]> {
    return this.usersRepository.find({
      order: { createdAt: 'DESC' },
    });
  }

  async findOne(id: string): Promise<User> {
    const user = await this.usersRepository.findOne({ where: { id } });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    return user;
  }

  async findByUsername(username: string): Promise<User | null> {
    return this.usersRepository.findOne({ where: { username } });
  }

  async findByEmail(email: string): Promise<User | null> {
    return this.usersRepository.findOne({ where: { email } });
  }

  async findByUsernameOrEmail(usernameOrEmail: string): Promise<User | null> {
    return this.usersRepository
      .createQueryBuilder('user')
      .where('user.username = :value', { value: usernameOrEmail })
      .orWhere('user.email = :value', { value: usernameOrEmail })
      .getOne();
  }

  async findOnline(limit = 30): Promise<User[]> {
    return this.usersRepository.find({
      where: { isOnline: true },
      order: { lastSeen: 'DESC' },
      take: limit,
    });
  }

  async findLastVisited(limit = 30): Promise<User[]> {
    return this.usersRepository.find({
      order: { lastVisited: 'DESC' },
      take: limit,
    });
  }

  async findTopRanked(limit = 30): Promise<User[]> {
    return this.usersRepository.find({
      order: { points: 'DESC' },
      take: limit,
    });
  }

  async create(data: Partial<User>): Promise<User> {
    const existingUsername = await this.findByUsername(data.username || '');
    if (existingUsername) {
      throw new ConflictException('Username already exists');
    }

    const existingEmail = await this.findByEmail(data.email || '');
    if (existingEmail) {
      throw new ConflictException('Email already exists');
    }

    const user = this.usersRepository.create(data);
    return this.usersRepository.save(user);
  }

  async update(id: string, data: Partial<User>): Promise<User> {
    const user = await this.findOne(id);
    Object.assign(user, data);
    return this.usersRepository.save(user);
  }

  async updatePresence(id: string, isOnline: boolean): Promise<User> {
    const user = await this.findOne(id);
    user.isOnline = isOnline;
    user.lastSeen = new Date();
    if (isOnline) user.lastVisited = new Date();
    return this.usersRepository.save(user);
  }

  async remove(id: string): Promise<{ success: boolean }> {
    const user = await this.findOne(id);
    await this.usersRepository.remove(user);
    return { success: true };
  }
}
