import { Injectable, NotFoundException } from "@nestjs/common";
import { InjectModel } from "@nestjs/sequelize";
import { Op } from "sequelize";
import * as _ from "lodash";
import { CreateUserDto } from "./dto/create-user.dto";
import { UserQueryDto } from "./dto/user.query.dto";
import { User } from "../../database/models/user.model";
import { UpdateUserDto } from "./dto/update-user.dto";

@Injectable()
export class UsersService {
  constructor(
    @InjectModel(User)
    private readonly userModel: typeof User,
  ) {}

  async create(payload: CreateUserDto): Promise<User> {
    const newUser = await this.userModel.create({
      phoneNumber: payload.phoneNumber,
      email: payload.email,
      active: payload.active,
      roleId: payload.roleId,
      password: payload.password,
      emailVerificationSentAt: new Date()
    });

    return this.findOne(newUser.id);
  }

  async count(query?: UserQueryDto): Promise<number> {
    return this.userModel.count({ where: this.createWhere(query) });
  }

  findAll(query?: UserQueryDto): Promise<User[]> {
    const { page, limit, order, orderBy } = query;
    return this.userModel.findAll({
      where: this.createWhere(query),
      limit,
      offset: (page - 1) * limit,
      order: [[orderBy, order]],
    });
  }

  findAllRaw(opts?: any): Promise<User[]> {
    return this.userModel.findAll(opts);
  }

  findOne(id: number): Promise<User> {
    return this.userModel.findOne({
      where: {
        id,
      },
    });
  }

  findBy(payload: { [x: string]: any }): Promise<User> {
    return this.userModel.findOne({
      where: payload,
    });
  }

  findByEmailOrPhone(payload: string): Promise<User> {
    return this.userModel.findOne({
      where: {
        [Op.or]: [
          {email: payload},
          {phoneNumber: payload},
        ]
      }
    });
  }

  findNoScope(option?: any): Promise<User[]> {
    return this.userModel.unscoped().findAll(option);
  }

  findOneNoScope(option?: any): Promise<User> {
    return this.userModel.unscoped().findOne(option);
  }

  async update(id: number, payload: UpdateUserDto): Promise<User> {
    const user = await this.findOne(id);
    if (!user) throw new NotFoundException("User not found");
    await user.update(payload);
    return user;
  }

  async delete(id: number): Promise<void> {
    const user = await this.findOne(id);
    if (!user) throw new NotFoundException("User not found");
    await user.destroy();
  }

  async verification(id: number, type: string): Promise<User> {
    const user = await this.findOne(id);
    if (!user) throw new NotFoundException("Data pengguna tidak ditemukan");

    if (type === "phone") {
      await user.update({
        active: true,
        phoneNumberVerificationAt: new Date()
      });
    } else {
      await user.update({
        active: true,
        emailVerificationAt: new Date()
      });
    }

    return user;
  }

  private createWhere(conditions) {
    const { page, limit, order, orderBy, active, ...rest } = conditions; // eslint-disable-line
    const where = _.isEmpty(rest) ? {} : { [Op.or]: { ...rest } };
    where["active"] = _.isUndefined(active) ? [true, false] : active;
    return where;
  }
}
