class PrismaQueryBuilder {
  private model: any;
  private query: Record<string, unknown>;
  private where: Record<string, unknown> = {};
  private selectFields: Record<string, boolean> | null = null;
  private includeRelations: Record<string, unknown> | null = null;
  private secretFields: string[] = [];

  constructor(model: any, query: Record<string, unknown>) {
    this.model = model;
    this.query = query;
  }

  setBaseQuery(conditions: Record<string, unknown>) {
    this.where = { ...this.where, ...conditions };
    return this;
  }

  setSecretFields(fields: string[]) {
    this.secretFields = fields;
    return this;
  }

  search(searchFields: string[]) {
    const search = this.query.search as string;
    if (search) {
      this.where.OR = searchFields.map((f) => ({ [f]: { contains: search, mode: 'insensitive' } }));
    }
    return this;
  }

  filter() {
    const exclude = ['search', 'sort', 'page', 'limit', 'fields'];
    Object.entries(this.query)
      .filter(([k]) => !exclude.includes(k))
      .forEach(([k, v]) => { this.where[k] = v; });
    return this;
  }

  sort() {
    return this;
  }

  paginate() {
    return this;
  }

  fields() {
    const fields = this.query.fields as string;
    if (fields) {
      this.selectFields = Object.fromEntries(
        fields.split(',').filter((f) => !this.secretFields.includes(f)).map((f) => [f, true]),
      );
    }
    return this;
  }

  include(relations: Record<string, unknown>) {
    this.includeRelations = relations;
    return this;
  }

  async execute() {
    const page = Number(this.query.page) || 1;
    const limit = Number(this.query.limit) || 10;
    return this.model.findMany({
      where: this.where,
      skip: (page - 1) * limit,
      take: limit,
      ...(this.selectFields && { select: this.selectFields }),
      ...(this.includeRelations && { include: this.includeRelations }),
    });
  }

  async countTotal() {
    const total = await this.model.count({ where: this.where });
    const page = Number(this.query.page) || 1;
    const limit = Number(this.query.limit) || 10;
    return { total, page, limit, totalPages: Math.ceil(total / limit) };
  }
}

export default PrismaQueryBuilder;
