export const DEFAULT_NAMESPACE = "applicaster.v2";

export function toObject(map: Map<string, any>): Record<string, any> {
  if (!map) return {};

  const entriesArray = Array.from(map.entries());

  return entriesArray.reduce((obj, [key, value]) => {
    if (value instanceof Map) {
      obj[key] = toObject(value);
    } else {
      obj[key] = value;
    }

    return obj;
  }, {});
}

export class StorageMock {
  items: Map<string, Map<string, string>>;

  constructor() {
    this.items = new Map();
  }

  __clear() {
    this.items = new Map();
  }

  async getItem(key: string, namespace = DEFAULT_NAMESPACE) {
    if (key.includes("fail")) {
      throw new Error("failed to get item");
    }

    return Promise.resolve(
      this.items.get(namespace || DEFAULT_NAMESPACE)?.get?.(key)
    );
  }

  async setItem(key: string, value: string, namespace = DEFAULT_NAMESPACE) {
    if (key.includes("fail")) {
      throw new Error("failed to set item");
    }

    if (!this.items.has(namespace)) {
      this.items.set(namespace || DEFAULT_NAMESPACE, new Map());
    }

    this.items.get(namespace || DEFAULT_NAMESPACE)?.set?.(key, value);

    return Promise.resolve(true);
  }

  async removeItem(key: string, namespace = DEFAULT_NAMESPACE) {
    if (key.includes("fail")) {
      throw new Error("failed to remove item");
    }

    this.items.get(namespace || DEFAULT_NAMESPACE)?.delete?.(key);

    return Promise.resolve(true);
  }
}

export class SecureStorageMock extends StorageMock {
  securedItems: Map<string, Map<string, string>>;

  constructor() {
    super();
    this.securedItems = new Map();
  }

  async getKeychainItem(key, namespace = DEFAULT_NAMESPACE) {
    if (key.includes("fail")) throw new Error("failed to get secure item");

    return Promise.resolve(
      this.securedItems.get(namespace || DEFAULT_NAMESPACE)?.get?.(key)
    );
  }

  async setKeychainItem(key, value, namespace = DEFAULT_NAMESPACE) {
    if (key.includes("fail")) throw new Error("failed to set secure item");

    if (!this.securedItems.has(namespace)) {
      this.securedItems.set(namespace || DEFAULT_NAMESPACE, new Map());
    }

    this.securedItems.get(namespace || DEFAULT_NAMESPACE)?.set?.(key, value);

    return Promise.resolve(true);
  }

  async removeKeychainItem(key, namespace = DEFAULT_NAMESPACE) {
    if (key.includes("fail")) throw new Error("failed to remove secure item");

    this.securedItems.get(namespace || DEFAULT_NAMESPACE)?.delete?.(key);

    return Promise.resolve(true);
  }
}
