import DatastoreOperation from "./DatastoreOperation";
import PebblebedModel from "../PebblebedModel";
import Core from "../Core";
import { isNumber } from "../utility/BasicUtils";
import { CreateMessage, throwError, warn } from "../Messaging";
import { Key, PathType } from "@google-cloud/datastore";

export default class DatastoreDelete<T> extends DatastoreOperation<T> {
  private dataObjects: any[];
  private deleteIds: Array<PathType | Key> = [];
  private useIds = false;
  private ignoreAnc = false;
  private usingKeys = false;

  constructor(model: PebblebedModel<T>, data?: T | T[]) {
    super(model);

    if (data) {
      if (Array.isArray(data)) {
        this.dataObjects = data;
      } else {
        this.dataObjects = [data];
      }
    } else {
      this.useIds = true;
    }
  }

  /*public id(id: string | number) {
    this.deleteIds = [id];
    return this;
  }

  public ids(ids: Array<string | number>) {
    this.deleteIds = ids;
    return this;
  }*/

  public idsOrKeys(idsOrKeys: string | number | Key | Array<string | number | Key>) {
    if (Array.isArray(idsOrKeys)) {
      this.deleteIds = idsOrKeys;
    } else {
      this.deleteIds = [idsOrKeys];
    }

    if (typeof this.deleteIds[0] === "object") {
      if ((this.deleteIds[0] as Key).kind === this.kind) {
        this.usingKeys = true;
      } else {
        throwError(CreateMessage.OPERATION_KEYS_WRONG(this.model, "DELETE"));
      }
    } else {
      this.deleteIds = this.deleteIds.map(id => {
        if (this.idType === "int" && isNumber(id)) {
          return Core.Instance.dsModule.int(id as number);
        } else if (this.idType === "string" && typeof id === "string") {
          if (id.length === 0) {
            throwError(CreateMessage.OPERATION_STRING_ID_EMPTY(this.model, "DELETE"));
          }

          return id;
        }

        throwError(CreateMessage.OPERATION_DATA_ID_TYPE_ERROR(this.model, "DELETE", id));
        return "";
      });
    }

    return this;
  }

  public ignoreDetectedAncestors() {
    this.ignoreAnc = true;
    return this;
  }

  public async run() {
    const baseKey: PathType[] = this.getBaseKey();
    let deleteKeys: Key[] = [];

    if (!this.useIds) {
      for (const data of this.dataObjects) {
        let setAncestors: PathType[] = baseKey;
        let id: PathType | null = null;
        const entityKey = data[Core.Instance.dsModule.KEY];

        if (this.hasIdProperty && data[this.idProperty!] != null) {
          switch (this.idType) {
            case "int":
              if (isNumber(data[this.idProperty!])) {
                id = Core.Instance.dsModule.int(data[this.idProperty!]);
              }
              break;
            case "string":
              if (typeof data[this.idProperty!] === "string") {
                if (data[this.idProperty!].length === 0) {
                  throwError(CreateMessage.OPERATION_STRING_ID_EMPTY(this.model, "DELETE"));
                }

                id = data[this.idProperty!];
              }
              break;
          }

          if (id == null) {
            throwError(
              CreateMessage.OPERATION_DATA_ID_TYPE_ERROR(this.model, "DELETE", data[this.idProperty!])
            );
          }
        } else if (entityKey != null) {
          if (entityKey.hasOwnProperty("id")) {
            id = Core.Instance.dsModule.int(entityKey.id);
          } else {
            id = entityKey.name;
          }
        } else {
          throwError(CreateMessage.DELETE_NO_DATA_IDS_ERROR);
        }

        if (entityKey && entityKey.parent && !this.ignoreAnc) {
          if (setAncestors.length === 0) {
            setAncestors = entityKey.parent.path;
          } else {
            const prevAncestors = entityKey.parent.path.toString();
            const nextAncestors = setAncestors.toString();

            if (prevAncestors !== nextAncestors) {
              warn(
                CreateMessage.OPERATION_CHANGED_ANCESTORS_WARNING(
                  this.model,
                  "DELETE",
                  prevAncestors,
                  nextAncestors
                )
              );
            }
          }
        }

        deleteKeys.push(this.createFullKey(setAncestors.concat([this.kind, id!]), entityKey));
      }
    } else if (this.usingKeys) {
      deleteKeys = this.deleteIds.map(this.augmentKey);
    } else {
      deleteKeys = (this.deleteIds as PathType[]).map(id => this.createFullKey(baseKey.concat(this.kind, id)));
    }

    let deleteResponse;

    if (this.transaction) {
      deleteResponse = await this.transaction.delete(deleteKeys);
    } else {
      deleteResponse = await Core.Instance.dsModule.delete(deleteKeys);
    }

    if (Core.Instance.cacheStore != null) {
      Core.Instance.cacheStore.flushEntitiesByKeys(deleteKeys);
    }

    return deleteResponse;
  }
}
