import sift, { Query } from "sift";
import { v4 as guid } from "uuid";
import record from ".../../generation/generators/record";
import { Guid } from "../../string";
import { Model } from "../../model";
import { entity, Entity, Repository, RESULT_CODE__ACCEPTED, RESULT_CODE__CREATED, RESULT_CODE__OK, StoreResult } from "../repository";

export type K = { _id: Guid };

async function* asyncIterable<T extends {}>(
  collection: Record<Guid, T>,
  query?: Query<T>
): AsyncGenerator<Entity<T>> {
  const test = sift(query || {});
  const keys = Object.keys(collection);
  for (const _id of keys) {
    const record = collection[_id];
    if (!record) {
      continue;
    }
    const entity = { _id, ...record };
    if (test(entity)) {
      yield Promise.resolve(entity);
    }
  }
}

export class EphemeralRepository<T extends {} = {}>
  implements Repository<T>
{
  constructor(public readonly model: Model<T>, collection?: Record<Guid, Entity<T>>) {
    this.collection = collection || {};
  }

  public readonly collection: Record<Guid, Entity<T>>;

  get count(): Promise<number> {
    return Promise.resolve(Object.keys(this.collection).length);
  }

  create(record: T): Promise<StoreResult<T>> {
    const ntt = entity(record);
    this.collection[ntt._id] = ntt;
    return Promise.resolve(<StoreResult<T>>{
      _id: ntt._id,
      rescode: RESULT_CODE__CREATED,
    });
  }

  delete(_id: string): Promise<StoreResult<T>> {
    const record = this.collection[_id];
    if (!record) {
      return Promise.resolve(<StoreResult<T>>{
        _id,
        rescode: RESULT_CODE__OK,
      });
    }
    delete this.collection[_id];
    return Promise.resolve(<StoreResult<T>>{
      _id,
      rescode: RESULT_CODE__ACCEPTED,
    });
  }

  find(_id: string): Promise<StoreResult<T>> {
    let entity!: Entity<T>;
    for (const ntt of Object.values(this.collection)) {
      if (ntt._id === _id) {
        entity = ntt;
        break;
      }
    }
    return Promise.resolve({
      _id,
      data: entity,
      rescode: RESULT_CODE__OK,
    });
  }

  /*
  grow(amount: number): Promise<void> {
    return new Promise(resolve => {
      const name = this.model.name;
      console.log(`Generating ${amount} fake ${name}...`);
      for (let i = 0; i < amount; i++) {
        Object.assign(this.collection, {
          [guid()]: <T>record(this.model.fields, getModelMap()),
        });
      }
      console.log(`${Object.keys(this.collection).length} ${name}s total.`);
      resolve();
    });
  }*/

  select(query?: Query<T>): Promise<AsyncIterable<Entity<T>>> {
    return Promise.resolve(asyncIterable<T>(this.collection, query));
  }

  shrink(amount: number): Promise<void> {
    return new Promise(resolve => {
      const keys = Object.keys(this.collection);
      keys
        .splice(Math.max(0, keys.length - amount))
        .forEach(key => delete this.collection[key]);
      resolve();
    });
  }

  update({
    _id,
    ...record
  }: Entity<Partial<T>>): Promise<StoreResult<T>> {
    Object.assign(this.collection, { [_id]: record });
    return Promise.resolve(<StoreResult<T>>{
      _id,
      rescode: RESULT_CODE__ACCEPTED,
    });
  }
}
