package visitroute import ( "context" "fmt" "math/rand" "strings" "sync" "time" b64 "encoding/base64" "bitbucket.org/nemt/nemt-portal-api/application/applicationservice" "bitbucket.org/nemt/nemt-portal-api/application/third/eligibility/bcbsi" "bitbucket.org/nemt/nemt-portal-api/application/tncservice" "bitbucket.org/nemt/nemt-portal-api/application/viewmodel" "bitbucket.org/nemt/nemt-portal-api/infra/auth" "bitbucket.org/nemt/nemt-portal-api/infra/config" "bitbucket.org/nemt/nemt-portal-api/server/router/routeutils" "bitbucket.org/nemt/nemt-portal-api/server/validation" "github.com/labstack/echo" uuid "github.com/satori/go.uuid" "googlemaps.github.io/maps" ) var ( instance *controller once sync.Once ) type controller struct { svc *applicationservice.Service cfg *config.Config bcbsi *bcbsi.Service tnc *tncservice.Service } func controllerInstance(svc *applicationservice.Service, cfg *config.Config, tnc *tncservice.Service) *controller { once.Do(func() { instance = &controller{ svc: svc, cfg: cfg, bcbsi: bcbsi.New(cfg), tnc: tnc, } }) return instance } func (c *controller) generatePassword(n int) string { const ( charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ) return c.stringWithCharset(n, charset) } func (c *controller) stringWithCharset(length int, charset string) string { b := make([]byte, length) var seededRand *rand.Rand = rand.New( rand.NewSource(time.Now().UnixNano())) for i := range b { b[i] = charset[seededRand.Intn(len(charset))] } return string(b) } func (c *controller) handleRide(ctx echo.Context) error { var ride viewmodel.RideRequest if err := ctx.Bind(&ride); err != nil { fmt.Println(err) return routeutils.ResponseAPIValidationError(ctx, "invalid parameters") } authUser, err := auth.GetUserDetail(ctx, c.cfg) if err != nil { return routeutils.HandleAPIError(ctx, err) } visit, err := c.svc.Visits.GetByUUID(ride.Visit.UUID, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } ride.Visit = visit user, err := c.svc.Users.GetByMemberID(*visit.User.Member) if err != nil { fmt.Println(err) return routeutils.HandleAPIError(ctx, err) } ride.Visit.User = user subscriber := "S" ride.Visit.User.Type = &subscriber var provider viewmodel.ProviderResp provider, err = c.svc.Provider.GetByUUID(ride.Visit.Provider.ProviderUUID, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } visit.Provider = provider eligibility := viewmodel.Eligibility{} eligibility.Provider.ProviderNPI = provider.InternalID eligibility.Provider.ProviderName = provider.OrganizatioName eligibility.TrackingID = c.rangeIn(1000000, 9999999) eligibility.Subscriber.SubscriberID = *ride.Visit.User.Member eligibility.Subscriber.PatientType = *ride.Visit.User.Type eligibility.Subscriber.Name.First = ride.Visit.User.First eligibility.Subscriber.Name.Last = ride.Visit.User.Last eligibility.Subscriber.DemographicInfo.DateOfBirth = *ride.Visit.User.BirthDate eligibility.Subscriber.DemographicInfo.Gender = *ride.Visit.User.Gender loc, _ := time.LoadLocation("America/Chicago") eligibility.ServiceInfo.DateOfService = time.Now().In(loc) eligibility.ServiceInfo.ServiceTypeCodes = []string{"30"} resp271, err := c.bcbsi.BXE.Get271(eligibility) if err != nil { fmt.Println(err) return routeutils.ResponseAPIValidationError(ctx, err.Error()) } address := viewmodel.Address{} header := resp271.Division.HealthCareEligibilityResponse.LoopHL0030[0].HL_0460[0].HL_0890[0].NM1_0920[0].N3_0950 body := resp271.Division.HealthCareEligibilityResponse.LoopHL0030[0].HL_0460[0].HL_0890[0].NM1_0920[0].N4_0960 zipCode := strings.TrimSpace(body.N403) address.AddressTypeName = "Home" address.AddressType = "home" address.Name = fmt.Sprintf("%s, %s", header.N301, body.N401) address.Address = fmt.Sprintf("%s, %s (%s)", header.N301, body.N401, zipCode) address.CreatedUserUUID = authUser.ID address.User = user address.Type = "provider" googleMapsAPI, err := maps.NewClient(maps.WithClientIDAndSignature("gme-bluecrossandblue1", "msqgD-jdqCyR0M_1u5C1HION5iI=")) if err != nil { fmt.Println("Error to instantiate googles api: ", err.Error()) return routeutils.HandleAPIError(ctx, err) } r := &maps.GeocodingRequest{ Address: address.Address + " " + body.N402 + ", " + zipCode, } result, err := googleMapsAPI.Geocode(context.Background(), r) if err != nil { fmt.Println("Error to instantiate googles api: ", err.Error()) return routeutils.HandleAPIError(ctx, err) } if len(result) > 0 { address.Latitude = result[0].Geometry.Location.Lat address.Longitude = result[0].Geometry.Location.Lng } if address.Latitude != 0 && address.Longitude != 0 { if len(user.Addresses) > 0 { for _, a := range user.Addresses { if a.AddressType == "home" { err := c.svc.Users.RemoveAddress(a.UUID) if err != nil { fmt.Println("Error to remove old address: ", err.Error()) return routeutils.HandleAPIError(ctx, err) } } } } address, err = c.svc.Users.SaveAddress(address) if err != nil { fmt.Println("Error to save address: ", err.Error()) return routeutils.HandleAPIError(ctx, err) } user.Addresses = append(user.Addresses, address) } homeAddress := viewmodel.Address{} for _, a := range visit.User.Addresses { if a.AddressType == "home" { homeAddress = a } } ride.CreateUserUUID = authUser.ID ride.Destination = viewmodel.Location{ ID: visit.Provider.ProviderUUID, Name: visit.Provider.OrganizatioName, Latitude: visit.Provider.Address.Latitude, Longitude: visit.Provider.Address.Longitude, Address: visit.Provider.Address.StreetAddress1, } ride.Origin = viewmodel.Location{ ID: homeAddress.UUID, Name: homeAddress.AddressTypeName, Latitude: homeAddress.Latitude, Longitude: homeAddress.Longitude, Address: homeAddress.Address, } ride.Notes = ride.Notes ride.Passenger.FirstName = visit.User.First ride.Passenger.LastName = " " ride.Passenger.PhoneNumber = *visit.User.PhoneNumber ride.RideType = "lyft" ride.VisitDate = &visit.VisitDatetime ride.VisitTime = &visit.VisitDatetime var resp viewmodel.RideRequest if ride.TripType.Key != "from_visit_call" { if ride.TripType.Key == "from_visit" { newOrigin := ride.Origin ride.Origin = ride.Destination ride.Destination = newOrigin } if c.cfg.LyftProd.UserUUID != authUser.ID { resp, err = c.tnc.Lyft.RequestRide(ride) } else { fmt.Println("In Production") resp, err = c.tnc.LyftProd.RequestRide(ride) } if err != nil { return routeutils.HandleAPIError(ctx, err) } if ride.TripType.Key == "from_visit" { newOrigin := resp.Origin resp.Origin = resp.Destination resp.Destination = newOrigin } resp.RideID = strings.Replace(resp.RideID, "s_", "", -1) } else { resp = ride UUID, _ := uuid.NewV4() resp.RideID = UUID.String() } if resp.Status == "scheduled" || ride.TripType.Key == "from_visit_call" { if ride.PickupTime == nil { currentDate := time.Now() ride.PickupTime = ¤tDate } requestMS := (ride.PickupTime.UnixNano() / int64(time.Millisecond)) generateDate := time.Now() generateMS := (generateDate.UnixNano() / int64(time.Millisecond)) resp.RequestAt = ride.PickupTime resp.RequestAtMS = &requestMS resp.GeneratedAt = &generateDate resp.GeneratedAtMS = &generateMS } resp.Passenger.FirstName = visit.User.First resp.Passenger.LastName = visit.User.Last resp.Passenger.PhoneNumber = *visit.User.PhoneNumber if resp.Passenger.ImageURL == nil { imageURL := " " resp.Passenger.ImageURL = &imageURL } resp.UserUUID = visit.User.ID if ride.TripType.Key == "from_visit" { resp.Origin.Name = ride.Destination.Name resp.Origin.ID = ride.Destination.ID resp.Destination.Name = ride.Origin.Name resp.Destination.ID = ride.Origin.ID } else { resp.Origin.Name = ride.Origin.Name resp.Origin.ID = ride.Origin.ID resp.Destination.Name = ride.Destination.Name resp.Destination.ID = ride.Destination.ID } resp.Distance = ride.Distance resp.Duration = ride.Duration resp.ETA = ride.ETA resp.PickupTime = ride.PickupTime resp.VisitDate = &ride.Visit.VisitDatetime resp.VisitTime = &ride.Visit.VisitDatetime resp.VisitExternalID = ride.Visit.ExternalID resp.CreateUserUUID = authUser.ID resp.Visit.TripType = ride.Visit.TripType resp.Visit = visit if resp.TripType.Key == "from_visit_call" { resp.Status = "willCall" resp.Passenger.UserID = &resp.UserUUID } resp.TripType.Key = ride.TripType.Key if ride.TripType.Key == "roundtrip" || ride.TripType.Key == "roundtrip_call" { resp.TripType.Key = "to_visit" } else if ride.TripType.Key == "from_visit_call" { resp.TripType.Key = "from_visit_call" } entity, err := c.svc.Rides.Save(resp) if err != nil { fmt.Println("Error to save first ride: ", err.Error()) return routeutils.HandleAPIError(ctx, err) } go func() { err = c.svc.Notification.SendNotification(resp.Status, entity, resp) if err != nil { fmt.Println("Error to notify user: ", err.Error()) } }() if ride.TripType.Key == "roundtrip" || ride.TripType.Key == "roundtrip_call" { newRide := ride if ride.TripType.Key == "roundtrip" { destination := newRide.Origin newRide.Origin = newRide.Destination newRide.Destination = destination if ride.ReturnTime != nil { newRide.PickupTime = ride.ReturnTime } scheduledRide := make(map[string]interface{}) scheduledRide["timestamp_ms"] = ride.PickupTime.Unix() newRide.ScheduledPickupRange = scheduledRide newRide.Passenger.FirstName = visit.User.First newRide.Passenger.LastName = " " newRide.Passenger.PhoneNumber = *visit.User.PhoneNumber if c.cfg.LyftProd.UserUUID != authUser.ID { newRide, err = c.tnc.Lyft.RequestRide(newRide) } else { fmt.Println("In Production") newRide, err = c.tnc.LyftProd.RequestRide(newRide) } if err != nil { return routeutils.HandleAPIError(ctx, err) } destination = newRide.Origin newRide.Origin = newRide.Destination newRide.Destination = destination if newRide.Error != "" { fmt.Println("Error to schedule a ride on lyft: ", newRide.Error, newRide.ErrorDescription) } else { fmt.Println("Ride Scheduled: ", newRide.Status) } newRide.TripType.Key = "from_visit" } else { newRide.TripType.Key = "from_visit_call" newRide.Status = "willCall" newRide.RideID = entity.UUID } newRide.Visit = visit newRide.PickupTime = ride.ReturnTime requestMS := (newRide.PickupTime.UnixNano() / int64(time.Millisecond)) generateDate := time.Now() generateMS := (generateDate.UnixNano() / int64(time.Millisecond)) newRide.RequestAt = newRide.PickupTime newRide.RequestAtMS = &requestMS newRide.GeneratedAt = &generateDate newRide.GeneratedAtMS = &generateMS newRide.Passenger.FirstName = visit.User.First newRide.Passenger.LastName = visit.User.Last newRide.Passenger.PhoneNumber = *visit.User.PhoneNumber newRide.Passenger.UserID = &ride.UserUUID if newRide.Passenger.ImageURL == nil { imageURL := " " newRide.Passenger.ImageURL = &imageURL } newRide.UserUUID = ride.UserUUID newRide.Origin.Name = ride.Destination.Name newRide.Origin.ID = ride.Destination.ID newRide.Destination.Name = ride.Origin.Name newRide.Destination.ID = ride.Origin.ID newRide.Distance = ride.Distance newRide.Duration = ride.Duration newRide.ETA = ride.ETA newRide.VisitDate = ride.VisitDate newRide.VisitTime = ride.VisitTime newRide.VisitExternalID = ride.VisitExternalID newRide.CreateUserUUID = authUser.ID roudtripRide, err := c.svc.Rides.Save(newRide) if err != nil { return routeutils.HandleAPIError(ctx, err) } go func() { err = c.svc.Notification.SendNotification(newRide.Status, roudtripRide, newRide) if err != nil { fmt.Println("Error to notify user: ", err.Error()) } }() } ctx.Response().Header().Set("Content-Type", "application/json") return routeutils.ResponseAPIOK(ctx, entity) } func (c *controller) rangeIn(low, hi int) string { result := low + rand.Intn(hi-low) return fmt.Sprintf("%v", result) } func (c *controller) handle(ctx echo.Context) error { var visit viewmodel.Visit if err := ctx.Bind(&visit); err != nil { fmt.Println(err) return routeutils.ResponseAPIValidationError(ctx, "invalid parameters") } authUser, err := auth.GetUserDetail(ctx, c.cfg) if err != nil { return routeutils.HandleAPIError(ctx, err) } var provider viewmodel.ProviderResp provider, err = c.svc.Provider.GetByNPI(visit.RawProvider.FivePartKeyGroups[0].ProviderNum, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } if provider.ProviderUUID == "" { org := viewmodel.Organization{ Type: viewmodel.OrganizationType{ Key: "provider", Name: "Provider", }, Name: visit.RawProvider.OrgName, Description: visit.RawProvider.OrgName, Main: false, Author: authUser, LastEditor: authUser, Reference: visit.RawProvider, } org, err = c.svc.Organization.AddOrganization(org, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } provider, err = c.svc.Provider.GetByOrganization(org.UUID, authUser) if err != nil { return routeutils.HandleAPIError(ctx, err) } } visit.Provider = provider if validationErrors := validation.ValidateVisit(&visit, &authUser); len(validationErrors) > 0 { return routeutils.ResponseAPICustomValidationError(ctx, "visit validation failed", validationErrors) } // eligibility := viewmodel.Eligibility{} // eligibility.Provider.ProviderNPI = provider.InternalID // eligibility.Provider.ProviderName = provider.OrganizatioName // eligibility.TrackingID = c.rangeIn(1000000, 9999999) // eligibility.Subscriber.SubscriberID = *visit.User.Member // eligibility.Subscriber.PatientType = *visit.User.Type // eligibility.Subscriber.Name.First = visit.User.First // eligibility.Subscriber.Name.Last = visit.User.Last // eligibility.Subscriber.DemographicInfo.DateOfBirth = *visit.User.BirthDate // eligibility.Subscriber.DemographicInfo.Gender = *visit.User.Gender // loc, _ := time.LoadLocation("America/Chicago") // eligibility.ServiceInfo.DateOfService = time.Now().In(loc) // eligibility.ServiceInfo.ServiceTypeCodes = []string{"30"} // resp, err := c.bcbsi.BXE.Get271(eligibility) // if err != nil { // fmt.Println("Error to get eligibility: ", err.Error()) // return routeutils.ResponseAPIValidationError(ctx, err.Error()) // } user, err := c.svc.Users.GetByMemberID(*visit.User.Member) if err != nil { fmt.Println("Error to get user by memberID: ", err.Error()) return routeutils.HandleAPIError(ctx, err) } if user.ID == "" { if len(user.Pass) == 0 { user.Pass = c.generatePassword(8) } else { pass, err := b64.StdEncoding.DecodeString(user.Pass) if err != nil { return routeutils.ResponseAPIAuthError(ctx, "Invalid password", false) } user.Pass = string(pass) } if user.BirthDate == nil || user.BirthDate.IsZero() { return routeutils.ResponseAPIAuthError(ctx, "birthdate is required", false) } if user.Member == nil || len(*user.Member) == 0 { return routeutils.ResponseAPIAuthError(ctx, "member is required", false) } if user.Gender == nil || len(*user.Gender) == 0 || (*user.Gender != "M" && *user.Gender != "F" && *user.Gender != "U") { return routeutils.ResponseAPIAuthError(ctx, "gender is required", false) } if len(user.Name) == 0 && len(user.First) == 0 && len(user.Last) == 0 { return routeutils.ResponseAPIAuthError(ctx, "name is required", false) } if len(user.First) != 0 && len(user.Last) != 0 { user.Name = fmt.Sprintf("%s %s", user.First, user.Last) } if user.PhoneNumber != nil && len(*user.PhoneNumber) == 10 && !strings.Contains(*user.PhoneNumber, "+1") { phoneNumber := fmt.Sprintf("+1%s", *user.PhoneNumber) user.PhoneNumber = &phoneNumber } profile, err := c.svc.Profile.GetByKey("US") if err != nil { fmt.Println("Error to get Profile: ", err.Error()) return routeutils.HandleAPIError(ctx, err) } user.Profiles = append(user.Profiles, profile) user, err = c.svc.Users.Create(user, authUser) if err != nil { fmt.Println("Error to create the user: ", err.Error()) return routeutils.HandleAPIError(ctx, err) } } else { if visit.User.Email != nil && len(*visit.User.Email) > 0 { user.Email = visit.User.Email } if visit.User.PhoneNumber != nil { if len(*visit.User.PhoneNumber) == 10 && !strings.Contains(*visit.User.PhoneNumber, "+1") { phoneNumber := fmt.Sprintf("+1%s", *visit.User.PhoneNumber) visit.User.PhoneNumber = &phoneNumber } user.PhoneNumber = visit.User.PhoneNumber } err = c.svc.Users.UpdateLogin(user) if err != nil { fmt.Println("Error to update login: ", err.Error()) return routeutils.HandleAPIError(ctx, err) } } // address := viewmodel.Address{} // header := resp.Division.HealthCareEligibilityResponse.LoopHL0030[0].HL_0460[0].HL_0890[0].NM1_0920[0].N3_0950 // body := resp.Division.HealthCareEligibilityResponse.LoopHL0030[0].HL_0460[0].HL_0890[0].NM1_0920[0].N4_0960 // zipCode := strings.TrimSpace(body.N403) // address.AddressTypeName = "Home" // address.AddressType = "home" // address.Name = fmt.Sprintf("%s, %s", header.N301, body.N401) // address.Address = fmt.Sprintf("%s, %s (%s)", header.N301, body.N401, zipCode) // address.CreatedUserUUID = authUser.ID // address.User = user // address.Type = "provider" // googleMapsAPI, err := maps.NewClient(maps.WithClientIDAndSignature("gme-bluecrossandblue1", "msqgD-jdqCyR0M_1u5C1HION5iI=")) // if err != nil { // fmt.Println("Error to instantiate googles api: ", err.Error()) // return routeutils.HandleAPIError(ctx, err) // } // r := &maps.GeocodingRequest{ // Address: address.Address + " " + body.N402 + ", " + zipCode, // } // result, err := googleMapsAPI.Geocode(context.Background(), r) // if err != nil { // fmt.Println("Error to instantiate googles api: ", err.Error()) // return routeutils.HandleAPIError(ctx, err) // } // if len(result) > 0 { // address.Latitude = result[0].Geometry.Location.Lat // address.Longitude = result[0].Geometry.Location.Lng // } // if address.Latitude != 0 && address.Longitude != 0 { // if len(user.Addresses) > 0 { // for _, a := range user.Addresses { // if a.AddressType == "home" { // err := c.svc.Users.RemoveAddress(a.UUID) // if err != nil { // fmt.Println("Error to remove old address: ", err.Error()) // return routeutils.HandleAPIError(ctx, err) // } // } // } // } // address, err = c.svc.Users.SaveAddress(address) // if err != nil { // fmt.Println("Error saving address: ", err.Error()) // return routeutils.HandleAPIError(ctx, err) // } // user.Addresses = append(user.Addresses, address) // } visit.TripType = viewmodel.TripType{ Key: "no_trip", Value: "No Trip", } visit.User = user visit.CreatedUser = authUser visit, err = c.svc.Visits.Save(visit) if err != nil { fmt.Println("Error saving visit: ", err.Error()) return routeutils.HandleAPIError(ctx, err) } return routeutils.ResponseAPIOK(ctx, visit) } func (c *controller) handleGetByID(ctx echo.Context) error { visit_uuid := ctx.Param("visit_uuid") if visit_uuid == "" { return routeutils.ResponseAPIValidationError(ctx, "visit_uuid param is mandatory") } authUser, err := auth.GetUserDetail(ctx, c.cfg) if err != nil { return routeutils.HandleAPIError(ctx, err) } resp, err := c.svc.Visits.GetByUUID(visit_uuid, authUser) if err != nil { fmt.Println("Error to get Visit: ", err.Error()) return routeutils.HandleAPIError(ctx, err) } return routeutils.ResponseAPIOK(ctx, resp) } func (c *controller) handleGetAll(ctx echo.Context) error { user, err := auth.GetUserDetail(ctx, c.cfg) if err != nil { return routeutils.HandleAPIError(ctx, err) } resp, err := c.svc.Visits.GetAll(user) if err != nil { return routeutils.HandleAPIError(ctx, err) } return routeutils.ResponseAPIOK(ctx, resp) }