import { Test, TestingModule } from '@nestjs/testing';
import { ForbiddenException } from '@nestjs/common';
import { GuardsService } from './guards.service';
import { PrismaService } from '../database/prisma.service';
import { AuditService } from '../audit/audit.service';
import { RequestUser } from '../common/request-user';

describe('GuardsService', () => {
  let service: GuardsService;
  let prisma: PrismaService;
  let audit: AuditService;

  const mockPrisma = {
    guard: {
      findMany: jest.fn()
    },
    user: {
      create: jest.fn()
    },
    estate: {
      findUnique: jest.fn()
    },
    $transaction: jest.fn()
  };

  const mockAudit = {
    record: jest.fn()
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        GuardsService,
        { provide: PrismaService, useValue: mockPrisma },
        { provide: AuditService, useValue: mockAudit }
      ]
    }).compile();

    service = module.get<GuardsService>(GuardsService);
    prisma = module.get<PrismaService>(PrismaService);
    audit = module.get<AuditService>(AuditService);

    jest.clearAllMocks();
    mockPrisma.estate.findUnique.mockResolvedValue({
      subscriptionPlan: { limits: { guards: 10 } },
      _count: { guards: 0 }
    });
  });

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

  describe('findAll', () => {
    it('should return guards for estate admin', () => {
      const user: RequestUser = {
        id: 'user-1',
        role: 'ESTATE_ADMIN',
        estateId: 'estate-1'
      };

      const mockGuards = [
        {
          id: 'guard-1',
          estateId: 'estate-1',
          name: 'Main Gate Guard',
          phone: '+2348000000004',
          deviceId: 'guard-device-001'
        }
      ];

      mockPrisma.guard.findMany.mockReturnValue(mockGuards);

      const result = service.findAll(user, 'estate-1');

      expect(result).toEqual(mockGuards);
      expect(prisma.guard.findMany).toHaveBeenCalled();
    });

    it('should throw error for cross-estate access', () => {
      const user: RequestUser = {
        id: 'user-1',
        role: 'ESTATE_ADMIN',
        estateId: 'estate-1'
      };

      expect(() => service.findAll(user, 'estate-2')).toThrow(ForbiddenException);
    });
  });

  describe('create', () => {
    it('should create guard with transaction', async () => {
      const user: RequestUser = {
        id: 'user-1',
        role: 'ESTATE_ADMIN',
        estateId: 'estate-1'
      };

      const dto = {
        name: 'Main Gate Guard',
        phone: '+2348000000004',
        deviceId: 'guard-device-001',
        estateId: 'estate-1'
      };

      const mockNewGuard = {
        id: 'guard-1',
        estateId: 'estate-1',
        userId: 'user-new',
        name: 'Main Gate Guard',
        phone: '+2348000000004',
        deviceId: 'guard-device-001',
        status: 'ACTIVE',
        createdAt: new Date(),
        updatedAt: new Date()
      };

      mockPrisma.$transaction.mockResolvedValue(mockNewGuard);
      mockAudit.record.mockResolvedValue(undefined);

      const result = await service.create(dto, user);

      expect(result).toEqual(mockNewGuard);
      expect(audit.record).toHaveBeenCalledWith(
        expect.objectContaining({
          action: 'GUARD_CREATED',
          estateId: 'estate-1'
        })
      );
    });

    it('should throw error when accessing different estate', async () => {
      const user: RequestUser = {
        id: 'user-1',
        role: 'ESTATE_ADMIN',
        estateId: 'estate-1'
      };

      const dto = {
        name: 'Main Gate Guard',
        phone: '+2348000000004',
        deviceId: 'guard-device-001',
        estateId: 'estate-2'
      };

      await expect(service.create(dto, user)).rejects.toThrow(ForbiddenException);
    });
  });
});
