import { Collection, Item, Model, Repository as VuexRepository } from '@vuex-orm/core'
import { Store } from 'vuex'
import * as _ from 'lodash'
import { $objectProperties } from '~/services/utils/objectProperties'
import { AnyJson } from '~/types/interfaces'
import {OrderByVuexOrm} from "~/types/types";

/**
 * Classe Wrapper pour assurer les opérations les plus communes des Repository de VuexORM
 */
class Repository {
  private store !: Store<any>

  /**
   * Set le store
   *
   * @param {Store<any>} store
   */
  public setStore (store: Store<any>) {
    this.store = store
  }

  /**
   * Récupération du repository du Model
   *
   * @param {Model} model
   * @return {VuexRepository<Model>} le repository
   */
  public getRepository (model: typeof Model): VuexRepository<Model> {
    return this.store.$repo(model)
  }

  public createNewModelInstance (model: typeof Model): Model {
    return this.getRepository(model).make()
  }

  /**
   * Récupération du nom de l'entité du model
   *
   * @param {Model} model
   * @return {string} l'entité
   */
  public getEntity (model: typeof Model): string {
    return this.getRepository(model).getModel().$entity()
  }

  /**
   * Créer une entry dans le repository
   *
   * @param {Model} model
   * @param {AnyJson} entry
   */
  public make (model: typeof Model, entry?: AnyJson): Model {
    return this.getRepository(model).make(entry)
  }

  /**
   * Persist l'entry dans le repository
   *
   * @param {Model} model
   * @param {AnyJson} entry
   */
  public persist (model: typeof Model, entry: AnyJson): Model {
    return this.getRepository(model).save(entry)
  }

  /**
   * Effectue une mise à jour du store après la modification d'un champ de l'entry
   *
   * @param {Model} model
   * @param {AnyJson} entry
   * @param {any} value
   * @param {string} field
   */
  public updateStoreFromField (model: typeof Model, entry: AnyJson, value: any, field: string): void {
    if (!_.has(entry, field)) { throw new Error('field not found') }

    entry[field] = value
    this.persist(model, $objectProperties.cloneAndNest(entry))
  }

  /**
   * Récupération de l'Item du Model souhaité
   *
   * @param {Model} model
   * @param {number|string} id
   * @return {Item} l'Item
   */
  public findItemFromModel (model: typeof Model, id: number|string): Item {
    const repository = this.getRepository(model)
    const item = repository.find(id)
    if (!item || typeof item === 'undefined') { throw new Error('Item not found') }

    return item
  }

  /**
   * Récupération de la Collection du Model souhaité
   *
   * @param {Model} model
   * @param {OrderByVuexOrm} orderBy
   * @return {Collection} la collection
   */
  public findCollectionFromModel (model: typeof Model, orderBy?: OrderByVuexOrm): Collection {
    const repository = this.getRepository(model)
    if(orderBy){
      for(const orderKey in orderBy){
        repository.orderBy(orderKey, orderBy[orderKey])
      }
    }
    return repository.all()
  }

  /**
   * Supprime l'Item du repository
   *
   * @param {Model} model
   * @param {number} id
   */
  public deleteItem (model: typeof Model, id: number|string) {
    const repository = this.getRepository(model)
    repository.destroy(id)
  }

  /**
   * Supprime tous les Items du repository
   *
   * @param {Model} model
   */
  public cleanRepository (model: typeof Model) {
    const repository = this.getRepository(model)
    repository.flush()
  }
}

export const repositoryHelper = new Repository()
