package usecase import ( "errors" "fmt" "time" "users_management/m/model/dto/req" "users_management/m/model/dto/res" "users_management/m/model/entity" "users_management/m/repository" "users_management/m/utils/helper" "users_management/m/utils/service" "github.com/go-playground/validator/v10" "github.com/google/uuid" ) type DeviceDetailsUseCase interface { CreateDeviceDetails(device req.DeviceDetailsDTO) error GetAllDeviceDetails() ([]res.DeviceDetailsResponse, error) GetDeviceDetailsByID(id uuid.UUID) (res.DeviceDetailsResponse, error) UpdateDeviceDetails(id uuid.UUID, device req.UpdateDeviceDetailsDTO) error DeleteDeviceDetails(id uuid.UUID) error // Port management // ValidatePortUsage(deviceID uuid.UUID, requiredPorts int) error RecalculatePortUsage(deviceID uuid.UUID) error AssignCustomerToPort(deviceID uuid.UUID, customerName string,portNumber *int) error UpdatePortUsage(deviceID uuid.UUID, portUsed int) error UpdatePortAssignments(deviceID uuid.UUID, portAssignments []req.PortAssignmentDTO) error } type deviceDetailsUseCase struct { deviceDetailsRepo repository.DeviceDetailsRepo geocoder service.GeocodingService validate *validator.Validate } func NewDeviceDetailsUseCase(deviceDetailsRepo repository.DeviceDetailsRepo, geocoder service.GeocodingService) DeviceDetailsUseCase { return &deviceDetailsUseCase{ deviceDetailsRepo: deviceDetailsRepo, geocoder: geocoder, validate: validator.New(), } } func (u *deviceDetailsUseCase) UpdatePortAssignments(deviceID uuid.UUID, assignments []req.PortAssignmentDTO) error { // Validate the assignments if len(assignments) == 0 { return errors.New("port assignments cannot be empty") } // Check for duplicate port numbers portNumbers := make(map[int]bool) for _, assignment := range assignments { if portNumbers[assignment.PortNumber] { return fmt.Errorf("duplicate port number: %d", assignment.PortNumber) } portNumbers[assignment.PortNumber] = true } return u.deviceDetailsRepo.UpdatePortAssignments(deviceID, assignments) } func (u *deviceDetailsUseCase) UpdatePortUsage(deviceID uuid.UUID, portUsed int) error { return u.deviceDetailsRepo.UpdatePortUsage(deviceID, portUsed) } func (u *deviceDetailsUseCase) CreateDeviceDetails(deviceDTO req.DeviceDetailsDTO) error { err := u.validate.Struct(deviceDTO) if err != nil { return fmt.Errorf("validation error: %w", err) } newDevice := entity.Device{ ID: uuid.New(), DeviceCode: deviceDTO.DeviceCode, DeviceType: entity.DeviceType(deviceDTO.DeviceType), Longitude: deviceDTO.Longitude, Latitude: deviceDTO.Latitude, PortAmount: deviceDTO.PortAmount, Status: entity.DeviceStatus(deviceDTO.Status), Province: deviceDTO.Province, City: deviceDTO.City, District: deviceDTO.District, CreatedAt: time.Now(), UpdatedAt: time.Now(), } return u.deviceDetailsRepo.Create(newDevice) } func (u *deviceDetailsUseCase) GetAllDeviceDetails() ([]res.DeviceDetailsResponse, error) { devices, err := u.deviceDetailsRepo.GetAll() if err != nil { return nil, err } return helper.ConvertToDeviceDetailsResponses(devices, u.geocoder) } func (u *deviceDetailsUseCase) GetDeviceDetailsByID(id uuid.UUID) (res.DeviceDetailsResponse, error) { device, err := u.deviceDetailsRepo.GetByID(id) if err != nil { return res.DeviceDetailsResponse{}, err } return helper.ConvertToDeviceDetailsResponse(device, u.geocoder) } func (u *deviceDetailsUseCase) UpdateDeviceDetails(id uuid.UUID, deviceDTO req.UpdateDeviceDetailsDTO) error { err := u.validate.Struct(deviceDTO) if err != nil { return fmt.Errorf("validation error: %w", err) } updates := map[string]interface{}{} if deviceDTO.DeviceCode != nil { updates["device_code"] = *deviceDTO.DeviceCode } if deviceDTO.DeviceType != nil { updates["device_type"] = *deviceDTO.DeviceType } if deviceDTO.Longitude != nil { updates["longitude"] = *deviceDTO.Longitude } if deviceDTO.Latitude != nil { updates["latitude"] = *deviceDTO.Latitude } if deviceDTO.PortAmount != nil { // Validate port amount change if *deviceDTO.PortAmount < 0 { return fmt.Errorf("port amount cannot be negative") } // If not setting to 0, check current usage if *deviceDTO.PortAmount > 0 { currentUsed, _, err := u.deviceDetailsRepo.GetPortUsageByDevice(id) if err != nil { return err } if *deviceDTO.PortAmount < currentUsed { return fmt.Errorf("cannot reduce port amount to %d, currently using %d ports", *deviceDTO.PortAmount, currentUsed) } } updates["port_amount"] = *deviceDTO.PortAmount } if deviceDTO.Status != nil { updates["status"] = *deviceDTO.Status } if deviceDTO.Region != nil { updates["region"] = *deviceDTO.Region } if deviceDTO.Province != nil { updates["province"] = *deviceDTO.Province } if deviceDTO.City != nil { updates["city"] = *deviceDTO.City } if deviceDTO.District != nil { updates["district"] = *deviceDTO.District } if len(updates) == 0 { return errors.New("no fields to update") } updates["updated_at"] = time.Now() return u.deviceDetailsRepo.Update(id, updates) } func (u *deviceDetailsUseCase) DeleteDeviceDetails(id uuid.UUID) error { // Check if device has connections backbones, err := u.deviceDetailsRepo.GetBackbonesByDeviceID(id) if err != nil { return err } if len(backbones) > 0 { return errors.New("cannot delete device with active backbone connections") } fishbones, err := u.deviceDetailsRepo.GetFishbonesByDeviceID(id) if err != nil { return err } if len(fishbones) > 0 { return errors.New("cannot delete device with active fishbone connections") } towers, err := u.deviceDetailsRepo.GetTowersByDeviceID(id) if err != nil { return err } if len(towers) > 0 { return errors.New("cannot delete device with active tower connections") } return u.deviceDetailsRepo.Delete(id) } func (u *deviceDetailsUseCase) RecalculatePortUsage(deviceID uuid.UUID) error { return u.deviceDetailsRepo.UpdateDevicePortUsage(deviceID) } func (u *deviceDetailsUseCase) AssignCustomerToPort(deviceID uuid.UUID, customerName string, portNumber *int) error { return u.deviceDetailsRepo.AssignCustomerToPort(deviceID, customerName, portNumber) }