import { describe, it, expect, beforeEach, vi } from "vitest";
import { PreferencesService } from "./preferences.service";
import type { PrismaService } from "../prisma/prisma.service";
import type { UpdatePreferencesDto } from "./dto";

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

  const mockPrisma = {
    userPreference: {
      findUnique: vi.fn(),
      create: vi.fn(),
      update: vi.fn(),
    },
  };

  const mockUserId = "user-uuid-123";

  const mockDbPreference = {
    id: "pref-uuid-456",
    userId: mockUserId,
    theme: "system",
    locale: "en",
    timezone: null,
    settings: {},
    updatedAt: new Date("2026-01-01T00:00:00Z"),
  };

  beforeEach(() => {
    service = new PreferencesService(mockPrisma as unknown as PrismaService);
    vi.clearAllMocks();
  });

  describe("getPreferences", () => {
    it("should return existing preferences", async () => {
      mockPrisma.userPreference.findUnique.mockResolvedValue(mockDbPreference);

      const result = await service.getPreferences(mockUserId);

      expect(result).toMatchObject({
        id: mockDbPreference.id,
        userId: mockUserId,
        theme: "system",
        locale: "en",
        timezone: null,
        settings: {},
      });
      expect(mockPrisma.userPreference.findUnique).toHaveBeenCalledWith({
        where: { userId: mockUserId },
      });
      expect(mockPrisma.userPreference.create).not.toHaveBeenCalled();
    });

    it("should create default preferences when none exist", async () => {
      mockPrisma.userPreference.findUnique.mockResolvedValue(null);
      mockPrisma.userPreference.create.mockResolvedValue(mockDbPreference);

      const result = await service.getPreferences(mockUserId);

      expect(result).toMatchObject({
        id: mockDbPreference.id,
        userId: mockUserId,
        theme: "system",
        locale: "en",
      });
      expect(mockPrisma.userPreference.create).toHaveBeenCalledWith({
        data: expect.objectContaining({
          userId: mockUserId,
          theme: "system",
          locale: "en",
        }),
      });
    });
  });

  describe("updatePreferences", () => {
    it("should update existing preferences", async () => {
      const updateDto: UpdatePreferencesDto = { theme: "dark", locale: "fr" };
      const updatedPreference = { ...mockDbPreference, theme: "dark", locale: "fr" };

      mockPrisma.userPreference.findUnique.mockResolvedValue(mockDbPreference);
      mockPrisma.userPreference.update.mockResolvedValue(updatedPreference);

      const result = await service.updatePreferences(mockUserId, updateDto);

      expect(result).toMatchObject({ theme: "dark", locale: "fr" });
      expect(mockPrisma.userPreference.update).toHaveBeenCalledWith({
        where: { userId: mockUserId },
        data: expect.objectContaining({ theme: "dark", locale: "fr" }),
      });
      expect(mockPrisma.userPreference.create).not.toHaveBeenCalled();
    });

    it("should create preferences when updating non-existent record", async () => {
      const updateDto: UpdatePreferencesDto = { theme: "light" };
      const createdPreference = { ...mockDbPreference, theme: "light" };

      mockPrisma.userPreference.findUnique.mockResolvedValue(null);
      mockPrisma.userPreference.create.mockResolvedValue(createdPreference);

      const result = await service.updatePreferences(mockUserId, updateDto);

      expect(result).toMatchObject({ theme: "light" });
      expect(mockPrisma.userPreference.create).toHaveBeenCalledWith({
        data: expect.objectContaining({
          userId: mockUserId,
          theme: "light",
        }),
      });
      expect(mockPrisma.userPreference.update).not.toHaveBeenCalled();
    });

    it("should handle timezone update", async () => {
      const updateDto: UpdatePreferencesDto = { timezone: "America/New_York" };
      const updatedPreference = { ...mockDbPreference, timezone: "America/New_York" };

      mockPrisma.userPreference.findUnique.mockResolvedValue(mockDbPreference);
      mockPrisma.userPreference.update.mockResolvedValue(updatedPreference);

      const result = await service.updatePreferences(mockUserId, updateDto);

      expect(result.timezone).toBe("America/New_York");
      expect(mockPrisma.userPreference.update).toHaveBeenCalledWith({
        where: { userId: mockUserId },
        data: expect.objectContaining({ timezone: "America/New_York" }),
      });
    });

    it("should handle settings update", async () => {
      const updateDto: UpdatePreferencesDto = { settings: { notifications: true } };
      const updatedPreference = { ...mockDbPreference, settings: { notifications: true } };

      mockPrisma.userPreference.findUnique.mockResolvedValue(mockDbPreference);
      mockPrisma.userPreference.update.mockResolvedValue(updatedPreference);

      const result = await service.updatePreferences(mockUserId, updateDto);

      expect(result.settings).toEqual({ notifications: true });
    });
  });
});
