package usecase import ( "math" "users_management/m/model/dto/req" "users_management/m/model/dto/res" "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 NearestDeviceUseCase interface { GetNearestDevices(request req.NearestDeviceDTO) ([]res.NearestDeviceResponse, error) GetNearestDeviceByID(id uuid.UUID, userLat, userLng float64) (res.NearestDeviceDetailResponse, error) GetNearestTowers(request req.NearestTowerDTO) ([]res.NearestTowerResponse, error) GetNearestTowerByID(id uuid.UUID, userLat, userLng float64) (res.NearestTowerDetailResponse, error) } type nearestDeviceUseCase struct { nearestDeviceRepo repository.NearestDeviceRepo geocoder service.GeocodingService validate *validator.Validate } func NewNearestDeviceUseCase(nearestDeviceRepo repository.NearestDeviceRepo, geocoder service.GeocodingService) NearestDeviceUseCase { return &nearestDeviceUseCase{ nearestDeviceRepo: nearestDeviceRepo, geocoder: geocoder, validate: validator.New(), } } func (u *nearestDeviceUseCase) GetNearestTowers(request req.NearestTowerDTO) ([]res.NearestTowerResponse, error) { err := u.validate.Struct(request) if err != nil { return nil, err } // Set defaults radius := request.Radius if radius == 0 { radius = 5.0 // Default 5km } limit := request.Limit if limit == 0 { limit = 10 // Default 10 towers } towers, err := u.nearestDeviceRepo.GetNearestTowers( request.Longitude, request.Latitude, radius, limit, request.Province, request.City, request.District, ) if err != nil { return nil, err } responses, err := helper.ConvertToNearestTowerResponses(towers, u.geocoder) if err != nil { return nil, err } return responses, nil } func (u *nearestDeviceUseCase) GetNearestTowerByID(id uuid.UUID, userLat, userLng float64) (res.NearestTowerDetailResponse, error) { tower, err := u.nearestDeviceRepo.GetTowerByIDWithConnections(id) if err != nil { return res.NearestTowerDetailResponse{}, err } // Calculate distance distance := calculateDistance(userLat, userLng, tower.Latitude, tower.Longitude) response, err := helper.ConvertToNearestTowerDetailResponse(tower, distance, u.nearestDeviceRepo, u.geocoder) if err != nil { return res.NearestTowerDetailResponse{}, err } return response, nil } func (u *nearestDeviceUseCase) GetNearestDevices(request req.NearestDeviceDTO) ([]res.NearestDeviceResponse, error) { err := u.validate.Struct(request) if err != nil { return nil, err } // Set defaults radius := request.Radius if radius == 0 { radius = 5.0 // Default 5km } limit := request.Limit if limit == 0 { limit = 10 // Default 10 devices } devices, err := u.nearestDeviceRepo.GetNearestDevices( request.Longitude, request.Latitude, radius, limit, request.Province, request.City, request.District, ) if err != nil { return nil, err } // Updated function call - removed userLat, userLng parameters since distance is already calculated responses, err := helper.ConvertToNearestDeviceResponses(devices, u.nearestDeviceRepo, u.geocoder) if err != nil { return nil, err } return responses, nil } func (u *nearestDeviceUseCase) GetNearestDeviceByID(id uuid.UUID, userLat, userLng float64) (res.NearestDeviceDetailResponse, error) { device, err := u.nearestDeviceRepo.GetDeviceByIDWithConnections(id) if err != nil { return res.NearestDeviceDetailResponse{}, err } // Calculate distance distance := calculateDistance(userLat, userLng, device.Latitude, device.Longitude) response, err := helper.ConvertToNearestDeviceDetailResponse(device, distance, u.nearestDeviceRepo, u.geocoder) if err != nil { return res.NearestDeviceDetailResponse{}, err } return response, nil } // calculateDistance calculates the distance between two coordinates using Haversine formula func calculateDistance(lat1, lng1, lat2, lng2 float64) float64 { const earthRadius = 6371 // Earth's radius in kilometers lat1Rad := lat1 * math.Pi / 180 lng1Rad := lng1 * math.Pi / 180 lat2Rad := lat2 * math.Pi / 180 lng2Rad := lng2 * math.Pi / 180 dlat := lat2Rad - lat1Rad dlng := lng2Rad - lng1Rad a := math.Sin(dlat/2)*math.Sin(dlat/2) + math.Cos(lat1Rad)*math.Cos(lat2Rad)*math.Sin(dlng/2)*math.Sin(dlng/2) c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a)) return earthRadius * c }