package analysis import ( "context" "encoding/json" "fmt" "time" "git.linuxforward.com/byom/byom-trends/common" "git.linuxforward.com/byom/byom-trends/logger" "git.linuxforward.com/byom/byom-trends/services/google" "git.linuxforward.com/byom/byom-trends/services/instagram" "git.linuxforward.com/byom/byom-trends/services/tiktok" "git.linuxforward.com/byom/byom-trends/services/youtube" "github.com/sirupsen/logrus" ) type TrendAnalyzer struct { instagramSvc *instagram.Client tiktokSvc *tiktok.Client youtubeSvc *youtube.Client googleSvc *google.Client logger *logrus.Entry } type EngagementMetrics struct { Platform string `json:"platform"` Engagement float64 `json:"engagement"` PostCount int64 `json:"post_count"` Followers int64 `json:"followers"` TotalLikes int64 `json:"total_likes"` TotalViews int64 `json:"total_views"` GrowthRate float64 `json:"growth_rate"` } type ContentTrends struct { TopHashtags map[string]int `json:"top_hashtags"` TopKeywords map[string]int `json:"top_keywords"` PopularTopics []string `json:"popular_topics"` BestPerforming map[string]string `json:"best_performing"` // platform -> content ID PostingTimes map[string]int `json:"posting_times"` // hour -> count } type AudienceMetrics struct { Demographics map[string]float64 `json:"demographics"` Interests []string `json:"interests"` Locations map[string]int `json:"locations"` ActiveHours map[int]int `json:"active_hours"` } func NewTrendAnalyzer( instagramSvc *instagram.Client, tiktokSvc *tiktok.Client, youtubeSvc *youtube.Client, googleSvc *google.Client, ) *TrendAnalyzer { return &TrendAnalyzer{ instagramSvc: instagramSvc, tiktokSvc: tiktokSvc, youtubeSvc: youtubeSvc, googleSvc: googleSvc, logger: logger.NewLogger("trend-analyzer"), } } func (a *TrendAnalyzer) AnalyzeEngagement(ctx context.Context, profile *common.SocialProfile) (*EngagementMetrics, error) { metrics := &EngagementMetrics{ Platform: profile.Platform, } switch profile.Platform { case "instagram": stats, err := a.instagramSvc.GetProfileStats(profile.Username) if err != nil { return nil, fmt.Errorf("get instagram stats: %w", err) } posts, err := a.instagramSvc.GetRecentPosts(profile.Username, 50) if err != nil { return nil, fmt.Errorf("get instagram posts: %w", err) } metrics.Engagement = a.instagramSvc.CalculateEngagement(posts) metrics.Followers = stats.FollowerCount case "tiktok": stats, err := a.tiktokSvc.GetProfileStats(profile.Username) if err != nil { return nil, fmt.Errorf("get tiktok stats: %w", err) } videos, err := a.tiktokSvc.GetRecentVideos(profile.Username, 50) if err != nil { return nil, fmt.Errorf("get tiktok videos: %w", err) } metrics.Engagement = a.tiktokSvc.CalculateEngagement(videos) metrics.Followers = stats.FollowerCount case "youtube": stats, err := a.youtubeSvc.GetChannelStats(profile.Username) if err != nil { return nil, fmt.Errorf("get youtube stats: %w", err) } videos, err := a.youtubeSvc.GetRecentVideos(profile.Username, 50) if err != nil { return nil, fmt.Errorf("get youtube videos: %w", err) } metrics.Engagement = a.youtubeSvc.CalculateEngagement(videos) metrics.Followers = stats.SubscriberCount } return metrics, nil } func (a *TrendAnalyzer) AnalyzeContent(ctx context.Context, profile *common.SocialProfile) (*ContentTrends, error) { trends := &ContentTrends{ TopHashtags: make(map[string]int), TopKeywords: make(map[string]int), PostingTimes: make(map[string]int), BestPerforming: make(map[string]string), } switch profile.Platform { case "instagram": posts, err := a.instagramSvc.GetRecentPosts(profile.Username, 100) if err != nil { return nil, fmt.Errorf("get instagram posts: %w", err) } // Analyze posting times for _, post := range posts { hour := post.Timestamp.Format("15") trends.PostingTimes[hour]++ } case "tiktok": videos, err := a.tiktokSvc.GetRecentVideos(profile.Username, 100) if err != nil { return nil, fmt.Errorf("get tiktok videos: %w", err) } trends.TopHashtags = a.tiktokSvc.GetTrendingHashtags(videos) case "youtube": videos, err := a.youtubeSvc.GetRecentVideos(profile.Username, 100) if err != nil { return nil, fmt.Errorf("get youtube videos: %w", err) } trends.TopHashtags = a.youtubeSvc.GetTrendingTags(videos) } // Get related topics from Google Trends topics, err := a.googleSvc.GetRelatedTopics(profile.Username) if err != nil { a.logger.WithError(err).Warn("failed to get related topics") } else { for _, topic := range topics { trends.PopularTopics = append(trends.PopularTopics, topic.Title) } } return trends, nil } func (a *TrendAnalyzer) AnalyzeAudience(ctx context.Context, profile *common.SocialProfile) (*AudienceMetrics, error) { metrics := &AudienceMetrics{ Demographics: make(map[string]float64), Locations: make(map[string]int), ActiveHours: make(map[int]int), } // Get regional interest from Google Trends locations, err := a.googleSvc.GetRegionalInterest(profile.Username, "") if err != nil { a.logger.WithError(err).Warn("failed to get regional interest") } else { metrics.Locations = locations } // Analyze active hours based on content posting times and engagement switch profile.Platform { case "instagram": posts, err := a.instagramSvc.GetRecentPosts(profile.Username, 100) if err != nil { return nil, fmt.Errorf("get instagram posts: %w", err) } for _, post := range posts { hour := post.Timestamp.Hour() metrics.ActiveHours[hour]++ } case "tiktok": videos, err := a.tiktokSvc.GetRecentVideos(profile.Username, 100) if err != nil { return nil, fmt.Errorf("get tiktok videos: %w", err) } for _, video := range videos { hour := video.CreateTime.Hour() metrics.ActiveHours[hour]++ } case "youtube": videos, err := a.youtubeSvc.GetRecentVideos(profile.Username, 100) if err != nil { return nil, fmt.Errorf("get youtube videos: %w", err) } for _, video := range videos { hour := video.PublishedAt.Hour() metrics.ActiveHours[hour]++ } } return metrics, nil } func (a *TrendAnalyzer) GenerateReport(ctx context.Context, profile *common.SocialProfile) (*common.TrendAnalysis, error) { engagement, err := a.AnalyzeEngagement(ctx, profile) if err != nil { return nil, fmt.Errorf("analyze engagement: %w", err) } content, err := a.AnalyzeContent(ctx, profile) if err != nil { return nil, fmt.Errorf("analyze content: %w", err) } audience, err := a.AnalyzeAudience(ctx, profile) if err != nil { return nil, fmt.Errorf("analyze audience: %w", err) } // Combine all analyses into a single report report := map[string]interface{}{ "engagement": engagement, "content": content, "audience": audience, "timestamp": time.Now(), } reportJSON, err := json.Marshal(report) if err != nil { return nil, fmt.Errorf("marshal report: %w", err) } analysis := &common.TrendAnalysis{ Type: "comprehensive", Data: reportJSON, } return analysis, nil }