import type { Query as PiniaOrmQuery } from 'pinia-orm'
import type { ApiFilter } from '~/types/data'
import type ApiResource from '~/models/ApiResource'
import PageFilter from '~/services/data/Filters/PageFilter'
import OrderBy from '~/services/data/Filters/OrderBy'

/**
 * A Query to filter and sort ApiResources.
 * Pass it to the `fetchCollection` method of the EntityManager to apply these filters to both
 * API fetch and PiniaOrm query, which allow to maintain this collection reactivity.
 */
export default class Query {
  protected filters: Array<ApiFilter> = []

  constructor(...filters: Array<ApiFilter>) {
    this.filters = filters
  }

  /**
   * Add an ApiFilter to the query
   *
   * @param filter
   */
  public add(filter: ApiFilter): this {
    this.filters.push(filter)
    return this
  }

  /**
   * Returns the URL's query in the Api Platform format.
   *
   * @see https://api-platform.com/docs/core/filters/
   */
  public getUrlQuery(): string {
    const queryParts: string[] = []

    this.filters.forEach((filter) => {
      const queryPart = filter.getApiQueryPart()
      if (queryPart) {
        queryParts.push(queryPart)
      }
    })

    return queryParts.filter((p) => !!p).join('&')
  }

  /**
   * Apply this query to the pinia orm query and return it.
   *
   * @see https://pinia-orm.codedredd.de/guide/repository/retrieving-data
   * @param query
   */
  public applyToPiniaOrmQuery(
    query: PiniaOrmQuery<ApiResource>,
  ): PiniaOrmQuery<ApiResource> {
    // 'Where' filters shall be applied first, then orderBy filters, and finally pagination
    const where: ApiFilter[] = []
    const orderBy: ApiFilter[] = []
    const pagination: ApiFilter[] = []

    this.filters.forEach((filter) => {
      if (filter instanceof PageFilter) {
        pagination.push(filter)
      } else if (filter instanceof OrderBy) {
        orderBy.push(filter)
      } else {
        where.push(filter)
      }
    })

    const filters = [...where, ...orderBy, ...pagination]

    filters.forEach((filter) => {
      query = filter.applyToPiniaOrmQuery(query)
    })

    return query
  }
}
