import { Err, Ok } from "..";
import { isResult, __internals } from "../guards";

describe("guards.ts", () => {
  /* eslint-disable @typescript-eslint/no-empty-function */
  const combinators = {
    and: () => {},
    andThen: () => {},
    andThenAsync: () => {},
    or: () => {},
    orElse: () => {},
    orElseAsync: () => {},
    yield: () => {},
    yieldAsync: () => {},
    yieldErr: () => {},
    yieldOr: () => {},
    yieldOrElse: () => {},
  };
  /* eslint-enable */

  const mockSuccess = {
    kind: "ok",
    _value: true,
    isOk: () => true,
    ...combinators,
  };

  const mockError = {
    kind: "err",
    _error: true,
    isErr: () => true,
    ...combinators,
  };

  describe("isResult", () => {
    it.each([mockError, mockSuccess, Err(true), Ok(true)])(
      "successfully narrows %s",
      (r) => {
        expect(isResult(r)).toBeTruthy();
      },
    );

    it.each([
      { kind: "ok", _value: true },
      prune(mockSuccess, "isOk"),
      prune(mockSuccess, "andThen", "orElse"),
      { kind: "err", _error: true },
      prune(mockError, "isErr"),
      prune(mockError, "andThen", "orElse"),
    ])("rejects %s correctly", (r) => {
      expect(isResult(r)).toBeFalsy();
    });
  });

  describe("isSuccess", () => {
    it.each([Ok(true), mockSuccess])("narrows %s successfully", (r) => {
      expect(__internals.isSuccess(r)).toBeTruthy();
    });

    it.each([
      Err(true),
      mockError,
      { kind: "ok", _value: true },
      prune(mockSuccess, "isOk"),
      prune(mockSuccess, "andThen", "orElse"),
    ])("rejects %s correctly", (r) => {
      expect(__internals.isSuccess(r)).toBeFalsy();
    });
  });

  describe("isError", () => {
    it.each([Err(true), mockError])("narrows %s successfully", (r) => {
      expect(__internals.isError(r)).toBeTruthy();
    });

    it.each([
      Ok(true),
      mockSuccess,
      { kind: "err", _error: true },
      prune(mockError, "isErr"),
      prune(mockError, "andThen", "orElse"),
    ])("rejects %s correctly", (r) => {
      expect(__internals.isError(r)).toBeFalsy();
    });
  });
});

function prune(u: object, ...fields: string[]): object {
  const copy = { ...u };
  for (const field of fields) {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    delete (copy as any)[field];
  }
  return copy;
}
