package bootstrap import ( "context" "encoding/json" "log" "time" "github.com/google/uuid" "github.com/yuxingu/digital-psychology/apps/api/internal/model" "github.com/yuxingu/digital-psychology/apps/api/internal/portrait" releng "github.com/yuxingu/digital-psychology/apps/api/internal/relation" "github.com/yuxingu/digital-psychology/apps/api/internal/repository" "github.com/yuxingu/digital-psychology/apps/api/internal/rhythm" "github.com/yuxingu/digital-psychology/apps/api/internal/star" "github.com/yuxingu/digital-psychology/apps/api/internal/star/synastry" ) // Service generates birthday-derived report bundles for a profile. type Service struct { Profiles *repository.ProfileRepo Reports *repository.ReportRepo Relations *repository.RelationRepo } // GenerateForProfile rebuilds solo reports for p and pair reports with counterparts. func (s *Service) GenerateForProfile(ctx context.Context, userID uuid.UUID, p *model.Profile) { if p == nil { return } s.genSolo(ctx, userID, p) list, err := s.Profiles.ListByUser(ctx, userID) if err != nil { log.Printf("bootstrap list profiles: %v", err) return } var self *model.Profile others := make([]*model.Profile, 0) for i := range list { item := list[i] if item.Relation == "self" { cp := item self = &cp } else if item.Relation == "other" { cp := item others = append(others, &cp) } } if self == nil { return } if p.Relation == "self" { for _, o := range others { s.genPair(ctx, userID, self, o) } return } s.genPair(ctx, userID, self, p) } func (s *Service) genSolo(ctx context.Context, userID uuid.UUID, p *model.Profile) { outP := portrait.Build(p.BirthDate, p.DisplayName) sum, _ := json.Marshal(outP.Summary) det, _ := json.Marshal(outP.Detail) if _, err := s.Reports.UpsertWithPeer(ctx, userID, p.ID, nil, "portrait", sum, det); err != nil { log.Printf("bootstrap portrait: %v", err) } now := time.Now().In(time.FixedZone("CST", 8*3600)) outS, err := star.BuildWith(star.BuildOpts{ Birth: p.BirthDate, BirthTime: p.BirthTime, BirthPlace: p.BirthPlace, Name: p.DisplayName, AsOf: now, }) if err != nil { log.Printf("bootstrap star: %v", err) } else { sum, _ = json.Marshal(outS.Summary) det, _ = json.Marshal(outS.Detail) if _, err := s.Reports.UpsertWithPeer(ctx, userID, p.ID, nil, "star", sum, det); err != nil { log.Printf("bootstrap star save: %v", err) } } outR := rhythm.BuildWith(p.BirthDate, p.DisplayName, now) sum, _ = json.Marshal(outR.Summary) det, _ = json.Marshal(outR.Detail) if _, err := s.Reports.UpsertWithPeer(ctx, userID, p.ID, nil, "rhythm", sum, det); err != nil { log.Printf("bootstrap rhythm: %v", err) } } func (s *Service) genPair(ctx context.Context, userID uuid.UUID, self, other *model.Profile) { if self == nil || other == nil || self.ID == other.ID { return } peer := other.ID relType := "" if other.RelationType != nil { relType = *other.RelationType } rel := releng.BuildFull(self.BirthDate, other.BirthDate, self.BirthTime, other.BirthTime, self.BirthPlace, other.BirthPlace, self.DisplayName, other.DisplayName, relType) sum, _ := json.Marshal(rel.Summary) det, _ := json.Marshal(rel.Detail) rep, err := s.Reports.UpsertWithPeer(ctx, userID, self.ID, &peer, "relation", sum, det) if err != nil { log.Printf("bootstrap relation: %v", err) } else if s.Relations != nil { _, _ = s.Relations.Create(ctx, userID, self.ID, other.ID, sum, rep.ID) } ca, err := star.NatalChart(self.BirthDate, self.BirthTime, self.BirthPlace) if err != nil { log.Printf("bootstrap synastry natal a: %v", err) return } cb, err := star.NatalChart(other.BirthDate, other.BirthTime, other.BirthPlace) if err != nil { log.Printf("bootstrap synastry natal b: %v", err) return } when := time.Now().In(time.FixedZone("CST", 8*3600)) out, err := synastry.BuildReport(ca, cb, self.DisplayName, other.DisplayName, when) if err != nil { log.Printf("bootstrap synastry: %v", err) return } sum, _ = json.Marshal(out.Summary) det, _ = json.Marshal(out.Detail) if _, err := s.Reports.UpsertWithPeer(ctx, userID, self.ID, &peer, "synastry", sum, det); err != nil { log.Printf("bootstrap synastry save: %v", err) } }