import type { TestingModule } from '@nestjs/testing';
import { Test } from '@nestjs/testing';
import { BadRequestException } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { ThemesService } from './themes.service';
import { PrismaService } from '../../prisma/prisma.service';
import { AVAILABLE_THEMES, DEFAULT_THEME_ID } from './theme-definitions';

describe('ThemesService', () => {
  let service: ThemesService;

  const mockPrisma = {
    platformTheme: {
      findFirst: jest.fn(),
      create: jest.fn(),
      findMany: jest.fn(),
    },
    resetTenantId: jest.fn(),
  };

  const mockEventEmitter = {
    emit: jest.fn(),
  };

  beforeEach(async () => {
    jest.clearAllMocks();

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        ThemesService,
        { provide: PrismaService, useValue: mockPrisma },
        { provide: EventEmitter2, useValue: mockEventEmitter },
      ],
    }).compile();

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

  // ---------------------------------------------------------------------------
  // getAvailableThemes
  // ---------------------------------------------------------------------------

  describe('getAvailableThemes', () => {
    it('returns all 10 theme definitions', () => {
      const themes = service.getAvailableThemes();
      expect(themes).toHaveLength(10);
      expect(themes.map((t) => t.id)).toContain('fitpulse-orange');
      expect(themes.map((t) => t.id)).toContain('crimson-power');
      expect(themes.map((t) => t.id)).toContain('cloud-professional');
    });
  });

  // ---------------------------------------------------------------------------
  // getCurrentThemeId
  // ---------------------------------------------------------------------------

  describe('getCurrentThemeId', () => {
    it('returns the stored theme ID when one exists', async () => {
      mockPrisma.platformTheme.findFirst.mockResolvedValue({
        id: 'uuid-1',
        themeId: 'crimson-power',
        setById: 'user-1',
        createdAt: new Date(),
      });

      const result = await service.getCurrentThemeId();

      expect(mockPrisma.resetTenantId).toHaveBeenCalled();
      expect(mockPrisma.platformTheme.findFirst).toHaveBeenCalledWith({
        orderBy: { createdAt: 'desc' },
      });
      expect(result).toBe('crimson-power');
    });

    it('returns the default theme ID when no theme is set', async () => {
      mockPrisma.platformTheme.findFirst.mockResolvedValue(null);

      const result = await service.getCurrentThemeId();

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

  // ---------------------------------------------------------------------------
  // getCurrent
  // ---------------------------------------------------------------------------

  describe('getCurrent', () => {
    it('returns themeId, theme definition, and isExplicit=true when set', async () => {
      mockPrisma.platformTheme.findFirst.mockResolvedValue({
        id: 'uuid-1',
        themeId: 'crimson-power',
        setById: 'user-1',
        createdAt: new Date(),
      });

      const result = await service.getCurrent();

      expect(result.themeId).toBe('crimson-power');
      expect(result.theme.id).toBe('crimson-power');
      expect(result.theme.name).toBe('Crimson Power');
      expect(result.theme.isDark).toBe(false);
      expect(result.isExplicit).toBe(true);
    });

    it('falls back to default theme with isExplicit=false when none is stored', async () => {
      mockPrisma.platformTheme.findFirst.mockResolvedValue(null);

      const result = await service.getCurrent();

      expect(result.themeId).toBe('fitpulse-orange');
      expect(result.theme.id).toBe('fitpulse-orange');
      expect(result.isExplicit).toBe(false);
    });
  });

  // ---------------------------------------------------------------------------
  // setActive
  // ---------------------------------------------------------------------------

  describe('setActive', () => {
    it('creates a new theme entry and emits event', async () => {
      mockPrisma.platformTheme.create.mockResolvedValue({
        id: 'uuid-2',
        themeId: 'pacific-teal',
        setById: 'admin-1',
        createdAt: new Date(),
      });

      const result = await service.setActive('pacific-teal', 'admin-1');

      expect(mockPrisma.resetTenantId).toHaveBeenCalled();
      expect(mockPrisma.platformTheme.create).toHaveBeenCalledWith({
        data: { themeId: 'pacific-teal', setById: 'admin-1' },
      });
      expect(result.themeId).toBe('pacific-teal');
      expect(result.theme.name).toBe('Pacific Teal');
      expect(mockEventEmitter.emit).toHaveBeenCalledWith(
        'theme.changed',
        expect.objectContaining({
          themeId: 'pacific-teal',
          setById: 'admin-1',
        }),
      );
    });

    it('throws BadRequestException for invalid theme ID', async () => {
      await expect(service.setActive('nonexistent-theme', 'admin-1')).rejects.toThrow(
        BadRequestException,
      );

      expect(mockPrisma.platformTheme.create).not.toHaveBeenCalled();
      expect(mockEventEmitter.emit).not.toHaveBeenCalled();
    });

    it('accepts all 10 valid theme IDs', async () => {
      for (const theme of AVAILABLE_THEMES) {
        mockPrisma.platformTheme.create.mockResolvedValue({
          id: `uuid-${theme.id}`,
          themeId: theme.id,
          setById: 'admin-1',
          createdAt: new Date(),
        });

        const result = await service.setActive(theme.id, 'admin-1');
        expect(result.themeId).toBe(theme.id);
      }

      expect(mockPrisma.platformTheme.create).toHaveBeenCalledTimes(10);
    });
  });

  // ---------------------------------------------------------------------------
  // getHistory
  // ---------------------------------------------------------------------------

  describe('getHistory', () => {
    it('returns theme change history ordered by createdAt desc', async () => {
      const history = [
        {
          id: 'uuid-3',
          themeId: 'royal-blue',
          setById: 'admin-1',
          createdAt: new Date('2026-02-20'),
        },
        {
          id: 'uuid-2',
          themeId: 'crimson-power',
          setById: 'admin-1',
          createdAt: new Date('2026-02-19'),
        },
        {
          id: 'uuid-1',
          themeId: 'fitpulse-orange',
          setById: null,
          createdAt: new Date('2026-02-18'),
        },
      ];
      mockPrisma.platformTheme.findMany.mockResolvedValue(history);

      const result = await service.getHistory();

      expect(mockPrisma.resetTenantId).toHaveBeenCalled();
      expect(mockPrisma.platformTheme.findMany).toHaveBeenCalledWith({
        orderBy: { createdAt: 'desc' },
        take: 20,
      });
      expect(result).toHaveLength(3);
      expect(result[0].themeId).toBe('royal-blue');
    });

    it('respects custom limit parameter', async () => {
      mockPrisma.platformTheme.findMany.mockResolvedValue([]);

      await service.getHistory(5);

      expect(mockPrisma.platformTheme.findMany).toHaveBeenCalledWith({
        orderBy: { createdAt: 'desc' },
        take: 5,
      });
    });
  });
});
