import { Test, TestingModule } from '@nestjs/testing';
import { NotFoundException } from '@nestjs/common';
import { getRepositoryToken } from '@nestjs/typeorm';
import { ClsService } from 'nestjs-cls';
import { SettingsService } from './settings.service';
import { TenantSettingsEntity } from '../../entities/tenant-settings.entity';
import { CLS_TENANT_ID, CLS_USER_ID } from '../../common/cls/cls.constants';

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

  const mockSettingsRepo = {
    findOne: jest.fn(),
    save: jest.fn(),
  };

  const mockCls = {
    get: jest.fn(),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        SettingsService,
        { provide: getRepositoryToken(TenantSettingsEntity), useValue: mockSettingsRepo },
        { provide: ClsService, useValue: mockCls },
      ],
    }).compile();

    service = module.get<SettingsService>(SettingsService);
    mockCls.get.mockImplementation((key: string) => {
      if (key === CLS_TENANT_ID) return 'tenant-1';
      if (key === CLS_USER_ID) return 'user-1';
      return null;
    });
  });

  afterEach(() => jest.clearAllMocks());

  describe('getBranding', () => {
    it('should return branding data when settings exist', async () => {
      mockSettingsRepo.findOne.mockResolvedValue({
        company_name: 'Test Co',
        logo_url: '/logo.png',
        primary_color: '#ff0000',
        secondary_color: '#00ff00',
      });

      const result = await service.getBranding();

      expect(result).toEqual({
        company_name: 'Test Co',
        logo_url: '/logo.png',
        primary_color: '#ff0000',
        secondary_color: '#00ff00',
      });
    });

    it('should return defaults when no settings found', async () => {
      mockSettingsRepo.findOne.mockResolvedValue(null);

      const result = await service.getBranding();

      expect(result).toEqual({
        company_name: null,
        logo_url: null,
        primary_color: '#10b981',
        secondary_color: '#1e293b',
      });
    });
  });

  describe('get', () => {
    it('should return full settings when found', async () => {
      const settings = { id: 's1', company_name: 'Test Co' };
      mockSettingsRepo.findOne.mockResolvedValue(settings);

      const result = await service.get();

      expect(result).toEqual(settings);
    });

    it('should throw NotFoundException when settings not found', async () => {
      mockSettingsRepo.findOne.mockResolvedValue(null);

      await expect(service.get()).rejects.toThrow(NotFoundException);
    });
  });

  describe('update', () => {
    it('should merge dto into settings and save', async () => {
      const existingSettings = { id: 's1', company_name: 'Old Name' };
      mockSettingsRepo.findOne.mockResolvedValue(existingSettings);
      mockSettingsRepo.save.mockResolvedValue(existingSettings);

      const result = await service.update({ company_name: 'New Name' } as any);

      expect(result).toEqual({ message: 'Settings updated successfully' });
      expect(mockSettingsRepo.save).toHaveBeenCalledWith(
        expect.objectContaining({ company_name: 'New Name', updated_by: 'user-1' }),
      );
    });

    it('should throw NotFoundException when settings not found', async () => {
      mockSettingsRepo.findOne.mockResolvedValue(null);

      await expect(service.update({} as any)).rejects.toThrow(NotFoundException);
    });
  });
});
