package usecase import ( "errors" "fmt" "log" "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 OLTUsecase interface { CreateOLT(oltDTO req.OLTDTO) error GetAllOLTs() ([]res.OLTResponse, error) GetOLTByID(id uuid.UUID) (res.OLTDetailResponse, error) UpdateOLT(id uuid.UUID, updateDTO req.UpdateOLTDTO) error DeleteOLT(id uuid.UUID) error AssignDeviceToOLT(oltID, deviceID uuid.UUID) error UnassignDeviceFromOLT(deviceID uuid.UUID) error GetDevicesByOLT(oltID uuid.UUID) ([]res.OLTDeviceResponse, error) } type oltUsecase struct { oltRepo repository.OLTRepo deviceRepo repository.DeviceDetailsRepo validate *validator.Validate geocoder service.GeocodingService } func NewOLTUsecase(oltRepo repository.OLTRepo, deviceRepo repository.DeviceDetailsRepo, geocoder service.GeocodingService) OLTUsecase { return &oltUsecase{ oltRepo: oltRepo, deviceRepo: deviceRepo, validate: validator.New(), geocoder: geocoder, } } func (u *oltUsecase) CreateOLT(oltDTO req.OLTDTO) error { if err := u.validate.Struct(oltDTO); err != nil { return fmt.Errorf("validation error: %w", err) } // Check if OLT name already exists existingOLT, err := u.oltRepo.GetByName(oltDTO.OLTName) if err == nil && existingOLT.ID != uuid.Nil { return errors.New("OLT name already exists") } olt := entity.OLT{ ID: uuid.New(), OLTName: oltDTO.OLTName, CreatedAt: time.Now(), UpdatedAt: time.Now(), } return u.oltRepo.Create(olt) } func (u *oltUsecase) GetAllOLTs() ([]res.OLTResponse, error) { olts, err := u.oltRepo.GetAll() if err != nil { return nil, err } var responses []res.OLTResponse for _, olt := range olts { response := res.OLTResponse{ ID: olt.ID, OLTName: olt.OLTName, CreatedAt: olt.CreatedAt, UpdatedAt: olt.UpdatedAt, } responses = append(responses, response) } return responses, nil } func (u *oltUsecase) GetOLTByID(id uuid.UUID) (res.OLTDetailResponse, error) { // First, check if OLT exists using the basic GetByID method olt, err := u.oltRepo.GetByID(id) if err != nil { log.Printf("OLT not found: %v", err) return res.OLTDetailResponse{}, errors.New("OLT not found") } log.Printf("OLT found: %+v", olt) // Try to get OLT with devices, but fallback to basic info if it fails oltWithDevices, err := u.oltRepo.GetByIDWithDevices(id) if err != nil { log.Printf("Error getting OLT with devices, using basic OLT info: %v", err) // Use the basic OLT info we already retrieved oltWithDevices = olt oltWithDevices.Devices = []entity.Device{} // Ensure devices is empty slice, not nil } log.Printf("OLT retrieved with devices: %+v", oltWithDevices) // Convert devices to device details responses var deviceResponses []res.DeviceDetailsResponse for _, device := range oltWithDevices.Devices { log.Printf("Processing device: %s", device.DeviceCode) // Get the device details using the existing method which properly loads DevicePort deviceDetails, err := u.deviceRepo.GetByID(device.ID) if err != nil { log.Printf("Error getting device details for %s: %v", device.DeviceCode, err) continue } deviceResponse, err := helper.ConvertToDeviceDetailsResponse(deviceDetails, u.geocoder) if err != nil { log.Printf("Error converting device response for %s: %v", device.DeviceCode, err) continue } deviceResponses = append(deviceResponses, deviceResponse) } // Initialize empty slice if nil to ensure JSON response shows empty array instead of null if deviceResponses == nil { deviceResponses = []res.DeviceDetailsResponse{} } response := res.OLTDetailResponse{ ID: oltWithDevices.ID, OLTName: oltWithDevices.OLTName, DeviceCount: len(deviceResponses), Devices: deviceResponses, CreatedAt: oltWithDevices.CreatedAt, UpdatedAt: oltWithDevices.UpdatedAt, } return response, nil } func (u *oltUsecase) UpdateOLT(id uuid.UUID, updateDTO req.UpdateOLTDTO) error { if err := u.validate.Struct(updateDTO); err != nil { return fmt.Errorf("validation error: %w", err) } // Check if OLT exists _, err := u.oltRepo.GetByID(id) if err != nil { return errors.New("OLT not found") } // Check if new name already exists (if name is being updated) if updateDTO.OLTName != nil { existingOLT, err := u.oltRepo.GetByName(*updateDTO.OLTName) if err == nil && existingOLT.ID != id { return errors.New("OLT name already exists") } } return u.oltRepo.Update(id, updateDTO) } func (u *oltUsecase) DeleteOLT(id uuid.UUID) error { // Check if OLT exists _, err := u.oltRepo.GetByID(id) if err != nil { return errors.New("OLT not found") } return u.oltRepo.Delete(id) } func (u *oltUsecase) AssignDeviceToOLT(oltID, deviceID uuid.UUID) error { return u.oltRepo.AssignDeviceToOLT(oltID, deviceID) } func (u *oltUsecase) UnassignDeviceFromOLT(deviceID uuid.UUID) error { return u.oltRepo.UnassignDeviceFromOLT(deviceID) } func (u *oltUsecase) GetDevicesByOLT(oltID uuid.UUID) ([]res.OLTDeviceResponse, error) { // Check if OLT exists _, err := u.oltRepo.GetByID(oltID) if err != nil { return nil, errors.New("OLT not found") } devices, err := u.oltRepo.GetDevicesByOLTID(oltID) if err != nil { return nil, err } var responses []res.OLTDeviceResponse for _, device := range devices { // Get address using geocoder address := "" if u.geocoder != nil { addr, err := u.geocoder.GetAddressFromCoordinates(device.Latitude, device.Longitude) if err == nil { address = addr } } // Get port usage from device port repository portUsed := 0 portUsageInfo, _, err := u.deviceRepo.GetPortUsageByDevice(device.ID) if err == nil { portUsed = portUsageInfo } response := res.OLTDeviceResponse{ ID: device.ID, DeviceCode: device.DeviceCode, DeviceType: string(device.DeviceType), Address: address, Province: device.Province, City: device.City, District: device.District, Status: string(device.Status), PortAmount: device.PortAmount, PortUsed: portUsed, ImageURL: device.ImageURL, CreatedAt: device.CreatedAt, UpdatedAt: device.UpdatedAt, } responses = append(responses, response) } return responses, nil }