import { ContextKeysManager } from "../../..";
import { StorageLevel } from "../../../consts";

describe("Context Keys Manager - setKey", () => {
  it("returns false if failed to saved with storage-level=default", async () => {
    // setup
    const key = "namespace.key";
    const value = "value";
    const storageLevel = StorageLevel.default;
    const error = Error("oops");

    const mockedLogger = {
      warn: jest.fn(),
    };

    const mockedSessionStorage = {
      getItem: jest.fn(),
      setItem: jest.fn().mockRejectedValueOnce(error),
      removeItem: jest.fn(),
    };

    const mockedLocalStorage = {
      getItem: jest.fn(),
      setItem: jest.fn(),
      removeItem: jest.fn(),
    };

    const mockedSecureStorage = {
      getItem: jest.fn(),
      setItem: jest.fn(),
      removeItem: jest.fn(),
    };

    const contextManager = new ContextKeysManager({
      logger: mockedLogger,
      sessionStorage: mockedSessionStorage,
      localStorage: mockedLocalStorage,
      secureStorage: mockedSecureStorage,
    });

    const setReferenceForKey = jest
      .spyOn(contextManager, "setReferenceForKey")
      .mockResolvedValueOnce(true);

    // run
    const result = await contextManager.setKey({ key, value, storageLevel });

    // verify
    expect(result).toBe(false);

    expect(mockedLogger.warn).toHaveBeenCalledTimes(1);

    expect(mockedLogger.warn).toHaveBeenCalledWith({
      message: error.message,
      data: { key, value, storageLevel },
    });

    expect(mockedSessionStorage.getItem).not.toHaveBeenCalled();

    expect(mockedSessionStorage.setItem).toHaveBeenCalledWith(
      "key",
      value,
      "namespace"
    );

    expect(mockedLocalStorage.getItem).not.toHaveBeenCalled();
    expect(mockedLocalStorage.setItem).not.toHaveBeenCalled();

    expect(mockedSecureStorage.getItem).not.toHaveBeenCalled();
    expect(mockedSecureStorage.setItem).not.toHaveBeenCalled();

    expect(setReferenceForKey).toHaveBeenCalledWith(
      "key",
      "namespace",
      storageLevel
    );
  });

  it("returns false if failed to saved with storage-level=persisted", async () => {
    // setup
    const key = "namespace.key";
    const value = "value";
    const storageLevel = StorageLevel.persisted;
    const error = Error("oops");

    const mockedLogger = {
      warn: jest.fn(),
    };

    const mockedSessionStorage = {
      getItem: jest.fn(),
      setItem: jest.fn(),
      removeItem: jest.fn(),
    };

    const mockedLocalStorage = {
      getItem: jest.fn(),
      setItem: jest.fn().mockRejectedValueOnce(error),
      removeItem: jest.fn(),
    };

    const mockedSecureStorage = {
      getItem: jest.fn(),
      setItem: jest.fn(),
      removeItem: jest.fn(),
    };

    const contextManager = new ContextKeysManager({
      logger: mockedLogger,
      sessionStorage: mockedSessionStorage,
      localStorage: mockedLocalStorage,
      secureStorage: mockedSecureStorage,
    });

    const setReferenceForKey = jest
      .spyOn(contextManager, "setReferenceForKey")
      .mockResolvedValueOnce(true);

    // run
    const result = await contextManager.setKey({ key, value, storageLevel });

    // verify
    expect(result).toBe(false);

    expect(mockedLogger.warn).toHaveBeenCalledTimes(1);

    expect(mockedLogger.warn).toHaveBeenCalledWith({
      message: error.message,
      data: { key, value, storageLevel },
    });

    expect(mockedSessionStorage.getItem).not.toHaveBeenCalled();
    expect(mockedSessionStorage.setItem).not.toHaveBeenCalled();

    expect(mockedLocalStorage.getItem).not.toHaveBeenCalled();

    expect(mockedLocalStorage.setItem).toHaveBeenCalledWith(
      "key",
      value,
      "namespace"
    );

    expect(mockedSecureStorage.getItem).not.toHaveBeenCalled();
    expect(mockedSecureStorage.setItem).not.toHaveBeenCalled();

    expect(setReferenceForKey).toHaveBeenCalledWith(
      "key",
      "namespace",
      storageLevel
    );
  });

  it("returns false if failed to saved with storage-level=secure", async () => {
    // setup
    const key = "namespace.key";
    const value = "value";
    const storageLevel = StorageLevel.secure;
    const error = Error("oops");

    const mockedLogger = {
      warn: jest.fn(),
    };

    const mockedSessionStorage = {
      getItem: jest.fn(),
      setItem: jest.fn(),
      removeItem: jest.fn(),
    };

    const mockedLocalStorage = {
      getItem: jest.fn(),
      setItem: jest.fn(),
      removeItem: jest.fn(),
    };

    const mockedSecureStorage = {
      getItem: jest.fn(),
      setItem: jest.fn().mockRejectedValueOnce(error),
      removeItem: jest.fn(),
    };

    const contextManager = new ContextKeysManager({
      logger: mockedLogger,
      sessionStorage: mockedSessionStorage,
      localStorage: mockedLocalStorage,
      secureStorage: mockedSecureStorage,
    });

    const setReferenceForKey = jest
      .spyOn(contextManager, "setReferenceForKey")
      .mockResolvedValueOnce(true);

    // run
    const result = await contextManager.setKey({ key, value, storageLevel });

    // verify
    expect(result).toBe(false);

    expect(mockedLogger.warn).toHaveBeenCalledTimes(1);

    expect(mockedLogger.warn).toHaveBeenCalledWith({
      message: error.message,
      data: { key, value, storageLevel },
    });

    expect(mockedSessionStorage.getItem).not.toHaveBeenCalled();
    expect(mockedSessionStorage.setItem).not.toHaveBeenCalled();

    expect(mockedLocalStorage.getItem).not.toHaveBeenCalled();
    expect(mockedLocalStorage.setItem).not.toHaveBeenCalled();

    expect(mockedSecureStorage.getItem).not.toHaveBeenCalled();

    expect(mockedSecureStorage.setItem).toHaveBeenCalledWith(
      "key",
      value,
      "namespace"
    );

    expect(setReferenceForKey).toHaveBeenCalledWith(
      "key",
      "namespace",
      storageLevel
    );
  });

  it("returns false if failed to saved with storage-level=undefined", async () => {
    // setup
    const key = "namespace.key";
    const value = "value";
    const error = Error("oops");

    const mockedLogger = {
      warn: jest.fn(),
    };

    const mockedSessionStorage = {
      getItem: jest.fn(),
      setItem: jest.fn().mockRejectedValueOnce(error),
      removeItem: jest.fn(),
    };

    const mockedLocalStorage = {
      getItem: jest.fn(),
      setItem: jest.fn(),
      removeItem: jest.fn(),
    };

    const mockedSecureStorage = {
      getItem: jest.fn(),
      setItem: jest.fn(),
      removeItem: jest.fn(),
    };

    const contextManager = new ContextKeysManager({
      logger: mockedLogger,
      sessionStorage: mockedSessionStorage,
      localStorage: mockedLocalStorage,
      secureStorage: mockedSecureStorage,
    });

    const setReferenceForKey = jest
      .spyOn(contextManager, "setReferenceForKey")
      .mockResolvedValueOnce(true);

    // run
    const result = await contextManager.setKey({ key, value });

    // verify
    expect(result).toBe(false);

    expect(mockedLogger.warn).toHaveBeenCalledTimes(1);

    expect(mockedLogger.warn).toHaveBeenCalledWith({
      message: error.message,
      data: { key, value, storageLevel: StorageLevel.default },
    });

    expect(mockedSessionStorage.getItem).not.toHaveBeenCalled();

    expect(mockedSessionStorage.setItem).toHaveBeenCalledWith(
      "key",
      value,
      "namespace"
    );

    expect(mockedLocalStorage.getItem).not.toHaveBeenCalled();
    expect(mockedLocalStorage.setItem).not.toHaveBeenCalled();

    expect(mockedSecureStorage.getItem).not.toHaveBeenCalled();
    expect(mockedSecureStorage.setItem).not.toHaveBeenCalled();

    expect(setReferenceForKey).toHaveBeenCalledWith(
      "key",
      "namespace",
      StorageLevel.default
    );
  });
});
