package helper import ( "fmt" "log" "math" "users_management/m/model/dto/res" "users_management/m/model/entity" "users_management/m/repository" "users_management/m/utils/service" "github.com/google/uuid" ) func ConvertToNearestDeviceResponses(devices []entity.DeviceWithDistance, repo repository.NearestDeviceRepo, geocoder service.GeocodingService) ([]res.NearestDeviceResponse, error) { var responses []res.NearestDeviceResponse for _, device := range devices { // Get address address := "" if geocoder != nil { addr, err := geocoder.GetAddressFromCoordinates(device.Latitude, device.Longitude) if err != nil { log.Printf("Geocoding error for device %s: %v", device.DeviceCode, err) address = fmt.Sprintf("Coordinates: %.6f, %.6f", device.Latitude, device.Longitude) } else { address = addr } } // Count connections backboneCount, fishboneCount, towerCount, err := repo.CountConnectionsByDeviceID(device.ID) if err != nil { log.Printf("Error counting connections for device %s: %v", device.DeviceCode, err) backboneCount, fishboneCount, towerCount = 0, 0, 0 } response := res.NearestDeviceResponse{ ID: device.ID, DeviceCode: device.DeviceCode, DeviceType: string(device.DeviceType), Distance: device.Distance, // Use the calculated distance from query Address: address, Longitude: device.Longitude, Latitude: device.Latitude, Status: string(device.Status), PortAmount: device.PortAmount, Region: device.Region, Province: device.Province, City: device.City, District: device.District, BackboneCount: backboneCount, FishboneCount: fishboneCount, TowerCount: towerCount, CreatedAt: device.CreatedAt, } responses = append(responses, response) } return responses, nil } func ConvertToNearestDeviceDetailResponse(device entity.Device, distance float64, repo repository.NearestDeviceRepo, geocoder service.GeocodingService) (res.NearestDeviceDetailResponse, error) { // Get address address := "" if geocoder != nil { addr, err := geocoder.GetAddressFromCoordinates(device.Latitude, device.Longitude) if err != nil { log.Printf("Geocoding error for device %s: %v", device.DeviceCode, err) address = fmt.Sprintf("Coordinates: %.6f, %.6f", device.Latitude, device.Longitude) } else { address = addr } } // Get detailed connection information backbones, err := repo.GetBackbonesByDeviceID(device.ID) if err != nil { return res.NearestDeviceDetailResponse{}, err } fishbones, err := repo.GetFishbonesByDeviceID(device.ID) if err != nil { return res.NearestDeviceDetailResponse{}, err } towers, err := repo.GetTowersByDeviceID(device.ID) if err != nil { return res.NearestDeviceDetailResponse{}, err } // Convert to response format backboneInfos := convertToBackboneConnectionInfos(backbones, device.ID) fishboneInfos := convertToFishboneConnectionInfos(fishbones, device.ID) towerInfos := convertToTowerConnectionInfos(towers, device.Latitude, device.Longitude) response := res.NearestDeviceDetailResponse{ ID: device.ID, DeviceCode: device.DeviceCode, DeviceType: string(device.DeviceType), Distance: distance, Address: address, Longitude: device.Longitude, Latitude: device.Latitude, Status: string(device.Status), PortAmount: device.PortAmount, Province: device.Province, City: device.City, District: device.District, ImageURL: device.ImageURL, Backbones: backboneInfos, Fishbones: fishboneInfos, Towers: towerInfos, CreatedAt: device.CreatedAt, UpdatedAt: device.UpdatedAt, } return response, nil } func convertToBackboneConnectionInfos(backbones []entity.Backbone, deviceID uuid.UUID) []res.BackboneConnectionInfo { var infos []res.BackboneConnectionInfo for _, backbone := range backbones { isStartDevice := backbone.DeviceStartID == deviceID connectedTo := "" if isStartDevice && backbone.DeviceEnd.DeviceCode != "" { connectedTo = backbone.DeviceEnd.DeviceCode } else if !isStartDevice && backbone.DeviceStart.DeviceCode != "" { connectedTo = backbone.DeviceStart.DeviceCode } info := res.BackboneConnectionInfo{ ID: backbone.ID, BackboneCode: backbone.BackboneCode, CoreAmount: backbone.CoreAmount, IsStartDevice: isStartDevice, ConnectedTo: connectedTo, } infos = append(infos, info) } return infos } func convertToFishboneConnectionInfos(fishbones []entity.Fishbone, deviceID uuid.UUID) []res.FishboneConnectionInfo { var infos []res.FishboneConnectionInfo for _, fishbone := range fishbones { isStartDevice := fishbone.DeviceStartID == deviceID connectedTo := "" if isStartDevice && fishbone.DeviceEnd.DeviceCode != "" { connectedTo = fishbone.DeviceEnd.DeviceCode } else if !isStartDevice && fishbone.DeviceStart.DeviceCode != "" { connectedTo = fishbone.DeviceStart.DeviceCode } info := res.FishboneConnectionInfo{ ID: fishbone.ID, FishboneCode: fishbone.FishboneCode, CoreAmount: fishbone.CoreAmount, BackboneCode: fishbone.Backbone.BackboneCode, IsStartDevice: isStartDevice, ConnectedTo: connectedTo, } infos = append(infos, info) } return infos } func convertToTowerConnectionInfos(towers []entity.Tower, deviceLat, deviceLng float64) []res.TowerConnectionInfo { var infos []res.TowerConnectionInfo for _, tower := range towers { // Calculate distance from device to tower distance := calculateDistance(deviceLat, deviceLng, tower.Latitude, tower.Longitude) info := res.TowerConnectionInfo{ ID: tower.ID, TowerCode: tower.TowerCode, Distance: distance, ImageURL: &tower.ImageURL, } infos = append(infos, info) } return infos } 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 }