import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
import { Test, TestingModule } from "@nestjs/testing";
import { PrismaService } from "./prisma.service";

describe("PrismaService", () => {
  let service: PrismaService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [PrismaService],
    }).compile();

    service = module.get<PrismaService>(PrismaService);
  });

  afterEach(async () => {
    await service.$disconnect();
  });

  it("should be defined", () => {
    expect(service).toBeDefined();
  });

  describe("onModuleInit", () => {
    it("should connect to the database", async () => {
      const connectSpy = vi
        .spyOn(service, "$connect")
        .mockResolvedValue(undefined);

      await service.onModuleInit();

      expect(connectSpy).toHaveBeenCalled();
    });

    it("should throw error if connection fails", async () => {
      const error = new Error("Connection failed");
      vi.spyOn(service, "$connect").mockRejectedValue(error);

      await expect(service.onModuleInit()).rejects.toThrow(error);
    });
  });

  describe("onModuleDestroy", () => {
    it("should disconnect from the database", async () => {
      const disconnectSpy = vi
        .spyOn(service, "$disconnect")
        .mockResolvedValue(undefined);

      await service.onModuleDestroy();

      expect(disconnectSpy).toHaveBeenCalled();
    });
  });

  describe("isHealthy", () => {
    it("should return true when database is accessible", async () => {
      vi.spyOn(service, "$queryRaw").mockResolvedValue([{ result: 1 }]);

      const result = await service.isHealthy();

      expect(result).toBe(true);
    });

    it("should return false when database is not accessible", async () => {
      vi
        .spyOn(service, "$queryRaw")
        .mockRejectedValue(new Error("Database error"));

      const result = await service.isHealthy();

      expect(result).toBe(false);
    });
  });

  describe("getConnectionInfo", () => {
    it("should return connection info when connected", async () => {
      const mockResult = [
        {
          current_database: "test_db",
          version: "PostgreSQL 17.0 on x86_64-linux",
        },
      ];
      vi.spyOn(service, "$queryRaw").mockResolvedValue(mockResult);

      const result = await service.getConnectionInfo();

      expect(result).toEqual({
        connected: true,
        database: "test_db",
        version: "PostgreSQL",
      });
    });

    it("should return connected false when result is empty", async () => {
      vi.spyOn(service, "$queryRaw").mockResolvedValue([]);

      const result = await service.getConnectionInfo();

      expect(result).toEqual({ connected: false });
    });

    it("should return connected false when query fails", async () => {
      vi
        .spyOn(service, "$queryRaw")
        .mockRejectedValue(new Error("Query failed"));

      const result = await service.getConnectionInfo();

      expect(result).toEqual({ connected: false });
    });

    it("should handle version without split", async () => {
      const mockResult = [
        {
          current_database: "test_db",
          version: "PostgreSQL",
        },
      ];
      vi.spyOn(service, "$queryRaw").mockResolvedValue(mockResult);

      const result = await service.getConnectionInfo();

      expect(result).toEqual({
        connected: true,
        database: "test_db",
        version: "PostgreSQL",
      });
    });
  });
});
