import { afterEach, describe, expect, it, vi } from "vitest";

import { AppException, ExceptionCode } from "@/core/errors";

import { Result, toError } from "../result";

afterEach(() => {
  vi.restoreAllMocks();
});

describe("Result error normalization", () => {
  it("normalizes unknown thrown values to AppException", async () => {
    const result = await Result.wrap(async () => {
      throw "boom";
    });

    expect(Result.isErr(result)).toBe(true);
    if (Result.isOk(result)) throw new Error("Expected Result.err");

    expect(result.error).toBeInstanceOf(AppException);
    expect(result.error.message).toBe("boom");
    expect((result.error as AppException).code).toBe(ExceptionCode.unknown);
  });

  it("returns AppException from toError", () => {
    const error = toError(new TypeError("Failed to fetch"));

    expect(error).toBeInstanceOf(AppException);
    expect(error.message).toBe("Network connection failed.");
    expect((error as AppException).code).toBe(ExceptionCode.network);
  });

  it("allows expected failures to be logged at info level", async () => {
    const info = vi.spyOn(console, "info").mockImplementation(() => undefined);
    const error = vi
      .spyOn(console, "error")
      .mockImplementation(() => undefined);

    const result = await Result.wrap(
      async () => {
        throw new Error("expected failure");
      },
      { errorLogLevel: "info" },
    );

    expect(Result.isErr(result)).toBe(true);
    expect(info).toHaveBeenCalledWith(
      expect.stringContaining("INFO [Result] Result.wrap caught exception"),
      expect.any(Object),
    );
    expect(error).not.toHaveBeenCalled();
  });
});
