import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
import { PrismaService } from '../../prisma/prisma.service';
import { JwtService } from '@nestjs/jwt';
import { EncryptionService } from '../../common/services/encryption.service';
import { ConflictException, UnauthorizedException } from '@nestjs/common';
import * as bcrypt from 'bcrypt';

describe('AuthService', () => {
  let service: AuthService;
  let prisma: PrismaService;
  let jwtService: JwtService;

  const mockPrisma = {
    business: {
      findUnique: jest.fn(),
      findFirst: jest.fn(),
      findMany: jest.fn().mockResolvedValue([]),
      create: jest.fn(),
      update: jest.fn(),
    },
  };

  const mockJwt = {
    sign: jest.fn().mockReturnValue('mocked-jwt-token'),
  };

  const mockEncryption = {
    encrypt: jest.fn().mockReturnValue('encrypted-text'),
    decrypt: jest.fn().mockReturnValue('decrypted-text'),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        AuthService,
        { provide: PrismaService, useValue: mockPrisma },
        { provide: JwtService, useValue: mockJwt },
        { provide: EncryptionService, useValue: mockEncryption },
      ],
    }).compile();

    service = module.get<AuthService>(AuthService);
    prisma = module.get<PrismaService>(PrismaService);
    jwtService = module.get<JwtService>(JwtService);
    jest.clearAllMocks();
  });

  describe('register', () => {
    it('should throw ConflictException if email already exists', async () => {
      mockPrisma.business.findUnique.mockResolvedValueOnce({ id: 'biz-1' });

      await expect(
        service.register({
          email: 'duplicate@test.com',
          password: 'Password123!',
          businessName: 'My Store',
        }),
      ).rejects.toThrow(ConflictException);
    });

    it('should register a new business successfully and return accessToken', async () => {
      mockPrisma.business.findUnique.mockResolvedValueOnce(null);
      mockPrisma.business.findFirst.mockResolvedValueOnce(null);
      mockPrisma.business.findMany.mockResolvedValueOnce([]);
      mockPrisma.business.create.mockResolvedValueOnce({
        id: 'new-biz-id',
        name: 'My Store',
        contactEmail: 'newstore@test.com',
        contactPhone: '+263771234567',
        status: 'ACTIVE',
        subscriptionStatus: 'TRIAL',
        subscriptionPlan: 'STARTER',
        onboardingStep: 'TRIAL_ACTIVE',
        trialEnd: new Date(),
      });

      const result = await service.register({
        email: 'newstore@test.com',
        password: 'Password123!',
        businessName: 'My Store',
        phone: '+263771234567',
      });

      expect(result.accessToken).toBe('mocked-jwt-token');
      expect(result.business.id).toBe('new-biz-id');
      expect(result.business.email).toBe('newstore@test.com');
    });
  });

  describe('login', () => {
    it('should throw UnauthorizedException if business not found', async () => {
      mockPrisma.business.findFirst.mockResolvedValueOnce(null);

      await expect(
        service.login({
          email: 'notfound@test.com',
          password: 'Password123!',
        }),
      ).rejects.toThrow(UnauthorizedException);
    });

    it('should throw UnauthorizedException if password does not match', async () => {
      const hashed = await bcrypt.hash('CorrectPassword123!', 10);
      mockPrisma.business.findFirst.mockResolvedValueOnce({
        id: 'biz-1',
        contactEmail: 'user@test.com',
        passwordHash: hashed,
      });

      await expect(
        service.login({
          email: 'user@test.com',
          password: 'WrongPassword!',
        }),
      ).rejects.toThrow(UnauthorizedException);
    });

    it('should return accessToken and business details on valid credentials', async () => {
      const password = 'CorrectPassword123!';
      const hashed = await bcrypt.hash(password, 10);
      mockPrisma.business.findFirst.mockResolvedValueOnce({
        id: 'biz-1',
        name: 'User Store',
        contactEmail: 'user@test.com',
        passwordHash: hashed,
        status: 'ACTIVE',
        subscriptionStatus: 'ACTIVE',
        subscriptionPlan: 'PRO',
      });

      const result = await service.login({
        email: 'user@test.com',
        password,
      });

      expect(result.accessToken).toBe('mocked-jwt-token');
      expect(result.business.name).toBe('User Store');
    });
  });

  describe('validateBusiness', () => {
    it('should return null if business does not exist', async () => {
      mockPrisma.business.findUnique.mockResolvedValueOnce(null);
      const result = await service.validateBusiness('nonexistent-id');
      expect(result).toBeNull();
    });

    it('should return business profile if found', async () => {
      mockPrisma.business.findUnique.mockResolvedValueOnce({
        id: 'biz-1',
        name: 'Test Store',
        contactEmail: 'store@test.com',
      });
      const result = await service.validateBusiness('biz-1');
      expect(result?.id).toBe('biz-1');
      expect(result?.name).toBe('Test Store');
    });
  });
});
