package repository import ( "context" "bugulma/backend/internal/domain" "bugulma/backend/internal/geospatial" "gorm.io/gorm" ) // ServiceRepository implements domain.ServiceRepository with GORM type ServiceRepository struct { *BaseRepository[domain.Service] } // NewServiceRepository creates a new GORM-based service repository func NewServiceRepository(db *gorm.DB) domain.ServiceRepository { return &ServiceRepository{ BaseRepository: NewBaseRepository[domain.Service](db), } } // GetByOrganization retrieves services by organization ID func (r *ServiceRepository) GetByOrganization(ctx context.Context, organizationID string) ([]*domain.Service, error) { return r.FindWhereWithContext(ctx, "organization_id = ?", organizationID) } // GetByType retrieves services by type func (r *ServiceRepository) GetByType(ctx context.Context, serviceType domain.ServiceType) ([]*domain.Service, error) { return r.FindWhereWithContext(ctx, "type = ?", serviceType) } // GetByDomain retrieves services by domain (case-insensitive partial match) func (r *ServiceRepository) GetByDomain(ctx context.Context, domainName string) ([]*domain.Service, error) { return r.FindWhereWithContext(ctx, "domain ILIKE ?", "%"+domainName+"%") } // SearchByDescription searches services by description or domain func (r *ServiceRepository) SearchByDescription(ctx context.Context, description string) ([]*domain.Service, error) { return r.FindWhereWithContext(ctx, "description ILIKE ? OR domain ILIKE ?", "%"+description+"%", "%"+description+"%") } // GetByServiceArea retrieves services by service area radius (basic implementation) func (r *ServiceRepository) GetByServiceArea(ctx context.Context, lat, lng, radiusKm float64) ([]*domain.Service, error) { // Basic implementation - in production, this would use geospatial queries // For now, return services with service area >= requested radius return r.FindWhereWithContext(ctx, "service_area_km >= ?", radiusKm) } // SearchWithLocation performs spatial + text search for services func (r *ServiceRepository) SearchWithLocation(ctx context.Context, query string, location *domain.Point, radiusKm float64) ([]*domain.Service, error) { db := r.DB().WithContext(ctx) // Build query with text search q := db.Model(&domain.Service{}) // Text search on domain, description, and search_keywords if query != "" { q = q.Where( "domain ILIKE ? OR description ILIKE ? OR search_keywords ILIKE ?", "%"+query+"%", "%"+query+"%", "%"+query+"%", ) } // Spatial search if location provided if location != nil && location.Valid && radiusKm > 0 { dialector := r.DB().Dialector.Name() if dialector == "postgres" { // Check if PostGIS is available var postgisAvailable bool var columnExists bool if err := r.DB().Raw("SELECT EXISTS(SELECT 1 FROM pg_extension WHERE extname = 'postgis')").Scan(&postgisAvailable).Error; err == nil && postgisAvailable { if err := r.DB().Raw(` SELECT EXISTS( SELECT 1 FROM information_schema.columns WHERE table_name = 'services' AND column_name = 'service_location' ) `).Scan(&columnExists).Error; err == nil && columnExists { // Use PostGIS spatial query via GeoHelper to ensure correct parameter ordering geo := geospatial.NewGeoHelper(r.DB()) q = q.Where( "service_location IS NOT NULL AND "+geo.DWithinExpr("service_location"), geo.PointRadiusArgs(location.Longitude, location.Latitude, radiusKm, false)..., ) } } } } var services []*domain.Service if err := q.Find(&services).Error; err != nil { return nil, err } return services, nil } // GetBySite retrieves services at a specific site func (r *ServiceRepository) GetBySite(ctx context.Context, siteID string) ([]*domain.Service, error) { return r.FindWhereWithContext(ctx, "site_id = ?", siteID) } // GetNearby retrieves services within a geographic radius func (r *ServiceRepository) GetNearby(ctx context.Context, lat, lng, radiusKm float64) ([]*domain.Service, error) { dialector := r.DB().Dialector.Name() if dialector == "postgres" { // Check if PostGIS is available var postgisAvailable bool var columnExists bool if err := r.DB().Raw("SELECT EXISTS(SELECT 1 FROM pg_extension WHERE extname = 'postgis')").Scan(&postgisAvailable).Error; err == nil && postgisAvailable { if err := r.DB().Raw(` SELECT EXISTS( SELECT 1 FROM information_schema.columns WHERE table_name = 'services' AND column_name = 'service_location' ) `).Scan(&columnExists).Error; err == nil && columnExists { // Use PostGIS for spatial query var services []*domain.Service query := ` SELECT * FROM services WHERE service_location IS NOT NULL AND ` + geospatial.NewGeoHelper(r.DB()).DWithinExpr("service_location") + ` ORDER BY ` + geospatial.NewGeoHelper(r.DB()).OrderByDistanceExpr("service_location") + ` ` args := geospatial.NewGeoHelper(r.DB()).PointRadiusArgs(lng, lat, radiusKm, true) result := r.DB().WithContext(ctx).Raw(query, args...).Scan(&services) if result.Error != nil { return nil, result.Error } return services, nil } } } // Fallback: return all services (spatial filtering not available) return r.GetAll(ctx) }