/**
 * @fileoverview Unit tests for WarehousesService
 * Tests cover CRUD operations for warehouses and locations
 */

import { Repository, SelectQueryBuilder } from 'typeorm';
import { Warehouse } from '../../warehouses/entities/warehouse.entity';
import { Location, LocationType } from '../entities/location.entity';

// Mock the AppDataSource before importing the service
jest.mock('../../../config/typeorm.js', () => ({
  AppDataSource: {
    getRepository: jest.fn(),
  },
}));

// Mock logger
jest.mock('../../../shared/utils/logger.js', () => ({
  logger: {
    info: jest.fn(),
    warn: jest.fn(),
    error: jest.fn(),
    debug: jest.fn(),
  },
}));

// Import after mocking
import { AppDataSource } from '../../../config/typeorm.js';

describe('WarehousesService', () => {
  let mockWarehouseRepository: Partial<Repository<Warehouse>>;
  let mockLocationRepository: Partial<Repository<Location>>;
  let mockQueryBuilder: Partial<SelectQueryBuilder<Warehouse>>;

  const mockTenantId = '550e8400-e29b-41d4-a716-446655440001';
  const mockCompanyId = '550e8400-e29b-41d4-a716-446655440002';
  const mockWarehouseId = '550e8400-e29b-41d4-a716-446655440010';

  const mockWarehouse: Partial<Warehouse> = {
    id: mockWarehouseId,
    tenantId: mockTenantId,
    companyId: mockCompanyId,
    name: 'Main Warehouse',
    code: 'WH-001',
    addressLine1: '123 Main Street',
    city: 'Mexico City',
    state: 'CDMX',
    country: 'MX',
    postalCode: '06600',
    phone: '+52-55-1234-5678',
    isActive: true,
    createdAt: new Date('2026-01-01'),
    updatedAt: new Date('2026-01-01'),
  };

  const mockLocation: Partial<Location> = {
    id: '550e8400-e29b-41d4-a716-446655440020',
    tenantId: mockTenantId,
    warehouseId: mockWarehouseId,
    name: 'Zone A - Shelf 1',
    locationType: LocationType.INTERNAL,
    parentId: null,
    active: true,
    createdAt: new Date('2026-01-01'),
    updatedAt: new Date('2026-01-01'),
  };

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

    // Setup mock query builder
    mockQueryBuilder = {
      leftJoinAndSelect: jest.fn().mockReturnThis(),
      where: jest.fn().mockReturnThis(),
      andWhere: jest.fn().mockReturnThis(),
      orderBy: jest.fn().mockReturnThis(),
      skip: jest.fn().mockReturnThis(),
      take: jest.fn().mockReturnThis(),
      getManyAndCount: jest.fn().mockResolvedValue([[mockWarehouse], 1]),
      getMany: jest.fn().mockResolvedValue([mockWarehouse]),
      getOne: jest.fn().mockResolvedValue(mockWarehouse),
      getCount: jest.fn().mockResolvedValue(1),
    };

    // Setup mock repositories
    mockWarehouseRepository = {
      create: jest.fn().mockReturnValue(mockWarehouse),
      save: jest.fn().mockResolvedValue(mockWarehouse),
      findOne: jest.fn().mockResolvedValue(mockWarehouse),
      find: jest.fn().mockResolvedValue([mockWarehouse]),
      update: jest.fn().mockResolvedValue({ affected: 1 }),
      delete: jest.fn().mockResolvedValue({ affected: 1 }),
      softDelete: jest.fn().mockResolvedValue({ affected: 1 }),
      createQueryBuilder: jest.fn().mockReturnValue(mockQueryBuilder),
    };

    mockLocationRepository = {
      create: jest.fn().mockReturnValue(mockLocation),
      save: jest.fn().mockResolvedValue(mockLocation),
      findOne: jest.fn().mockResolvedValue(mockLocation),
      find: jest.fn().mockResolvedValue([mockLocation]),
      update: jest.fn().mockResolvedValue({ affected: 1 }),
      softDelete: jest.fn().mockResolvedValue({ affected: 1 }),
    };

    // Configure AppDataSource mock
    (AppDataSource.getRepository as jest.Mock).mockImplementation((entity) => {
      if (entity === Warehouse || entity.name === 'Warehouse') {
        return mockWarehouseRepository;
      }
      if (entity === Location || entity.name === 'Location') {
        return mockLocationRepository;
      }
      return {};
    });
  });

  describe('Warehouse CRUD Operations', () => {
    it('should find all warehouses', async () => {
      const { warehousesService } = await import('../warehouses.service.js');

      // Service signature: findAll(tenantId, filters)
      const result = await warehousesService.findAll(mockTenantId, {
        companyId: mockCompanyId,
        page: 1,
        limit: 20,
      });

      expect(mockWarehouseRepository.createQueryBuilder).toHaveBeenCalled();
      expect(result.data).toBeDefined();
      expect(result.total).toBe(1);
    });

    it('should find warehouse by ID', async () => {
      const { warehousesService } = await import('../warehouses.service.js');

      // Service signature: findById(id, tenantId)
      const result = await warehousesService.findById(
        mockWarehouseId,
        mockTenantId
      );

      expect(mockWarehouseRepository.createQueryBuilder).toHaveBeenCalled();
      expect(result).toBeDefined();
      expect(result.id).toBe(mockWarehouseId);
    });

    it('should throw NotFoundError when warehouse not found', async () => {
      mockQueryBuilder.getOne = jest.fn().mockResolvedValue(null);

      const { warehousesService } = await import('../warehouses.service.js');

      await expect(
        warehousesService.findById('non-existent-id', mockTenantId)
      ).rejects.toThrow();
    });

    it('should create a new warehouse', async () => {
      const createDto = {
        name: 'Secondary Warehouse',
        code: 'WH-002',
        addressLine1: '456 Second Street',
        city: 'Guadalajara',
        state: 'Jalisco',
        country: 'MX',
      };

      const { warehousesService } = await import('../warehouses.service.js');

      // Service signature: create(dto, tenantId, userId)
      const result = await warehousesService.create(
        createDto,
        mockTenantId,
        'mock-user-id'
      );

      expect(mockWarehouseRepository.create).toHaveBeenCalled();
      expect(mockWarehouseRepository.save).toHaveBeenCalled();
      expect(result).toBeDefined();
    });

    it('should update an existing warehouse', async () => {
      const updateDto = {
        name: 'Updated Warehouse Name',
        phone: '+52-55-9999-8888',
      };

      const { warehousesService } = await import('../warehouses.service.js');

      // Service signature: update(id, dto, tenantId, userId)
      const result = await warehousesService.update(
        mockWarehouseId,
        updateDto,
        mockTenantId,
        'mock-user-id'
      );

      expect(mockWarehouseRepository.createQueryBuilder).toHaveBeenCalled();
      expect(mockWarehouseRepository.save).toHaveBeenCalled();
    });

    it('should delete a warehouse', async () => {
      const { warehousesService } = await import('../warehouses.service.js');

      // Service signature: delete(id, tenantId)
      await warehousesService.delete(mockWarehouseId, mockTenantId);

      // Service uses .delete() not .softDelete()
      expect(mockWarehouseRepository.delete).toHaveBeenCalled();
    });
  });

  describe('Location Operations', () => {
    it('should get warehouse locations', async () => {
      const { warehousesService } = await import('../warehouses.service.js');

      // Service signature: getLocations(warehouseId, tenantId)
      const result = await warehousesService.getLocations(
        mockWarehouseId,
        mockTenantId
      );

      expect(mockLocationRepository.find).toHaveBeenCalled();
      expect(result).toBeDefined();
    });

    // TODO: Method removed, update test
    // it('should create a location in warehouse', async () => {
    //   const createLocationDto = {
    //     name: 'Zone B - Shelf 1',
    //     code: 'WH-001/B/1',
    //     locationType: LocationType.INTERNAL,
    //   };
    //
    //   const { warehousesService } = await import('../warehouses.service.js');
    //
    //   const result = await warehousesService.createLocation(
    //     mockTenantId,
    //     mockCompanyId,
    //     mockWarehouseId,
    //     createLocationDto
    //   );
    //
    //   expect(mockLocationRepository.create).toHaveBeenCalled();
    //   expect(mockLocationRepository.save).toHaveBeenCalled();
    // });
  });

  describe('Validation', () => {
    it('should validate unique warehouse code', async () => {
      mockWarehouseRepository.findOne = jest.fn()
        .mockResolvedValueOnce(mockWarehouse);

      const createDto = {
        name: 'Duplicate Warehouse',
        code: 'WH-001', // Same code
      };

      expect(mockWarehouseRepository.findOne).toBeDefined();
    });
  });

  describe('Active/Inactive Status', () => {
    it('should filter only active warehouses', async () => {
      const { warehousesService } = await import('../warehouses.service.js');

      // Service signature: findAll(tenantId, filters)
      const result = await warehousesService.findAll(mockTenantId, {
        isActive: true,
      });

      expect(mockWarehouseRepository.createQueryBuilder).toHaveBeenCalled();
    });

    it('should deactivate a warehouse', async () => {
      const { warehousesService } = await import('../warehouses.service.js');

      // Service signature: update(id, dto, tenantId, userId)
      const result = await warehousesService.update(
        mockWarehouseId,
        { isActive: false },
        mockTenantId,
        'mock-user-id'
      );

      expect(mockWarehouseRepository.save).toHaveBeenCalled();
    });
  });
});
