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" "github.com/go-playground/validator/v10" "github.com/google/uuid" ) type FishboneUseCase interface { CreateFishbone(fishbone req.FishboneDTO) error GetAllFishbone() ([]res.FishboneResponse, error) GetByID(id uuid.UUID) (res.FishboneDetailResponse, error) UpdateFishbone(id uuid.UUID, fishbone req.UpdateFishboneDTO) error DeleteFishbone(id uuid.UUID) error GetFishboneStats() (map[string]interface{}, error) } type fishboneUseCase struct { fishboneRepo repository.FishboneRepo backboneRepo repository.BackboneRepo deviceDetailsRepo repository.DeviceDetailsRepo // Add this field validate *validator.Validate } func NewFishboneUseCase(fishboneRepo repository.FishboneRepo, backboneRepo repository.BackboneRepo, deviceDetailsRepo repository.DeviceDetailsRepo) FishboneUseCase { return &fishboneUseCase{ fishboneRepo: fishboneRepo, backboneRepo: backboneRepo, deviceDetailsRepo: deviceDetailsRepo, // Initialize the field validate: validator.New(), } } func (u *fishboneUseCase) CreateFishbone(fishbone req.FishboneDTO) error { err := u.validate.Struct(fishbone) if err != nil { return fmt.Errorf("validation error: %w", err) } // Validate that both devices exist and are ODP type startExists, err := u.deviceDetailsRepo.CheckDeviceExists(fishbone.DeviceStartID) if err != nil { return fmt.Errorf("error checking start device: %w", err) } if !startExists { return fmt.Errorf("start device does not exist") } endExists, err := u.deviceDetailsRepo.CheckDeviceExists(fishbone.DeviceEndID) if err != nil { return fmt.Errorf("error checking end device: %w", err) } if !endExists { return fmt.Errorf("end device does not exist") } // Validate port availability for ODP devices (each core needs 1 port) if err := u.deviceDetailsRepo.ValidatePortAvailability(fishbone.DeviceStartID, fishbone.CoreAmount); err != nil { return fmt.Errorf("start device: %w", err) } if err := u.deviceDetailsRepo.ValidatePortAvailability(fishbone.DeviceEndID, fishbone.CoreAmount); err != nil { return fmt.Errorf("end device: %w", err) } newFishbone := entity.Fishbone{ ID: uuid.New(), FishboneCode: fishbone.FishboneCode, BackboneID: fishbone.BackboneID, DeviceStartID: fishbone.DeviceStartID, DeviceEndID: fishbone.DeviceEndID, CoreAmount: fishbone.CoreAmount, CreatedAt: time.Now(), UpdatedAt: time.Now(), } // Create fishbone err = u.fishboneRepo.Post(newFishbone) if err != nil { return err } // Update port usage for both devices u.deviceDetailsRepo.UpdateDevicePortUsage(fishbone.DeviceStartID) u.deviceDetailsRepo.UpdateDevicePortUsage(fishbone.DeviceEndID) return nil } func (u *fishboneUseCase) GetAllFishbone() ([]res.FishboneResponse, error) { fishbones, err := u.fishboneRepo.GetAll() if err != nil { return nil, err } return helper.ConvertToSimpleFishboneResponses(fishbones), nil } func (u *fishboneUseCase) GetByID(id uuid.UUID) (res.FishboneDetailResponse, error) { fishbone, err := u.fishboneRepo.GetByIDWithRelations(id) if err != nil { return res.FishboneDetailResponse{}, err } // Convert to detailed response using helper return helper.ConvertToFishboneDetailResponse(fishbone), nil } func (u *fishboneUseCase) UpdateFishbone(id uuid.UUID, fishbone req.UpdateFishboneDTO) error { err := u.validate.Struct(fishbone) if err != nil { return fmt.Errorf("validation error: %w", err) } // Get original fishbone for comparison originalFishbone, err := u.fishboneRepo.GetByID(id) if err != nil { return err } updates := make(map[string]interface{}) // Track devices that need port recalculation devicesToUpdate := make(map[uuid.UUID]bool) devicesToUpdate[originalFishbone.DeviceStartID] = true devicesToUpdate[originalFishbone.DeviceEndID] = true // If core amount is changed, validate port availability if fishbone.CoreAmount != nil && *fishbone.CoreAmount != originalFishbone.CoreAmount { coreDiff := *fishbone.CoreAmount - originalFishbone.CoreAmount if coreDiff > 0 { // Increasing cores - check port availability if err := u.deviceDetailsRepo.ValidatePortAvailability(originalFishbone.DeviceStartID, coreDiff); err != nil { return fmt.Errorf("start device: %w", err) } if err := u.deviceDetailsRepo.ValidatePortAvailability(originalFishbone.DeviceEndID, coreDiff); err != nil { return fmt.Errorf("end device: %w", err) } } updates["core_amount"] = *fishbone.CoreAmount } if fishbone.DeviceStartID != nil { exists, err := u.deviceDetailsRepo.CheckDeviceExists(*fishbone.DeviceStartID) if err != nil { return fmt.Errorf("error checking new start device: %w", err) } if !exists { return fmt.Errorf("new start device does not exist") } coreAmount := originalFishbone.CoreAmount if fishbone.CoreAmount != nil { coreAmount = *fishbone.CoreAmount } if err := u.deviceDetailsRepo.ValidatePortAvailability(*fishbone.DeviceStartID, coreAmount); err != nil { return fmt.Errorf("new start device: %w", err) } updates["dev_start_id"] = *fishbone.DeviceStartID devicesToUpdate[*fishbone.DeviceStartID] = true } if fishbone.DeviceEndID != nil { exists, err := u.deviceDetailsRepo.CheckDeviceExists(*fishbone.DeviceEndID) if err != nil { return fmt.Errorf("error checking new end device: %w", err) } if !exists { return fmt.Errorf("new end device does not exist") } coreAmount := originalFishbone.CoreAmount if fishbone.CoreAmount != nil { coreAmount = *fishbone.CoreAmount } if err := u.deviceDetailsRepo.ValidatePortAvailability(*fishbone.DeviceEndID, coreAmount); err != nil { return fmt.Errorf("new end device: %w", err) } updates["dev_end_id"] = *fishbone.DeviceEndID devicesToUpdate[*fishbone.DeviceEndID] = true } if fishbone.BackboneID != nil { updates["backbone_id"] = *fishbone.BackboneID } if fishbone.FishboneCode != nil { updates["fishbone_code"] = *fishbone.FishboneCode } if len(updates) == 0 { return fmt.Errorf("no fields to update") } updates["updated_at"] = time.Now() // Update fishbone err = u.fishboneRepo.Update(id, updates) if err != nil { return err } // Recalculate port usage for all affected devices for deviceID := range devicesToUpdate { u.deviceDetailsRepo.UpdateDevicePortUsage(deviceID) } return nil } func (u *fishboneUseCase) DeleteFishbone(id uuid.UUID) error { // Check if fishbone exists exists, err := u.fishboneRepo.CheckFishboneExists(id) if err != nil { return err } if !exists { return errors.New("fishbone not found") } return u.fishboneRepo.Delete(id) } func (u *fishboneUseCase) GetFishboneStats() (map[string]interface{}, error) { fishbones, err := u.fishboneRepo.GetAll() if err != nil { return nil, err } return helper.GetFishboneStats(fishbones), nil }