import {
  FindManyOptions,
  FindOptionsOrder,
  FindOptionsSelect,
  FindOptionsWhere,
  LessThan,
  Repository,
} from 'typeorm';
import { frontDay } from './utils';

/**
 * 基础service类
 */
export abstract class BaseService<T> {
  protected repository: Repository<T>;

  save(data: Partial<T & { createDate: any; updateDate: any }>) {
    if (data.createDate == null) {
      data.createDate = Date.now();
    }
    if (data.updateDate == null) {
      data.updateDate = Date.now();
    }
    return this.repository.save(data as any) as Promise<T>;
  }

  find(
    where: FindOptionsWhere<T>[] | FindOptionsWhere<T>,
    select?: FindOptionsSelect<T>,
  ) {
    return this.repository.findOne({
      where,
      select,
    });
  }

  findById(id: number, select?: FindOptionsSelect<T>) {
    return this.repository.findOne({
      where: {
        id,
      } as any,
      select,
    });
  }

  findAll(
    where: FindOptionsWhere<T>[] | FindOptionsWhere<T>,
    select?: FindOptionsSelect<T>,
  ) {
    return this.repository.find({
      where,
      select,
    });
  }

  customFind(options: FindManyOptions<T>) {
    return this.repository.find(options);
  }

  findByIdAndUid(id: number, uid: number, select?: FindOptionsSelect<T>) {
    return this.repository.findOne({
      where: {
        id,
        uid,
      } as any,
      select,
    });
  }

  findPage(
    pagination: Partial<Pagination & FindOptionsWhere<T>>,
    select?: FindOptionsSelect<T>,
    order?: FindOptionsOrder<T>,
  ) {
    const { pageNum, pageSize, ...params } = pagination;
    return this.repository.find({
      where: params as any,
      skip: pageNum * pageSize,
      take: pageSize,
      select,
      order,
    });
  }

  findPageTotal(
    pagination: Partial<Pagination & FindOptionsWhere<T>>,
    select?: FindOptionsSelect<T>,
    order?: FindOptionsOrder<T>,
  ) {
    const { pageNum, pageSize, ...params } = pagination;
    return this.repository.findAndCount({
      where: params as any,
      skip: pageNum * pageSize,
      take: pageSize,
      select,
      order,
    });
  }

  update(id: number, data: Partial<T & { updateDate: any }>) {
    if (data.updateDate == null) {
      data.updateDate = Date.now();
    }
    return this.repository.update(id, data as any);
  }

  softDelete(id: number) {
    return this.repository.softDelete(id);
  }

  delete(id: number | number[]) {
    return this.repository.delete(id);
  }

  deleteExpire(day: number) {
    return this.repository.delete({
      updateDate: LessThan(frontDay(day)),
    } as any);
  }

  findIncludeDelete(
    where: FindOptionsWhere<T>[] | FindOptionsWhere<T>,
    select?: FindOptionsSelect<T>,
  ) {
    return this.repository.findOne({
      where,
      select,
      withDeleted: true,
    });
  }
}
