use super::super::accounts::DEFAULT_ACCOUNT_ID;
use super::super::DbPool;
use super::content_scores::{
get_avg_reply_engagement_for, get_avg_tweet_engagement_for, get_performance_counts_for,
get_top_topics_for, ContentScore,
};
use super::snapshots::get_follower_snapshots_for;
use crate::error::StorageError;
use chrono::{NaiveDate, Utc};
#[derive(Debug, Clone, serde::Serialize)]
pub struct FollowerSummary {
pub current: i64,
pub change_7d: i64,
pub change_30d: i64,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct ActionsSummary {
pub replies: i64,
pub tweets: i64,
pub threads: i64,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct EngagementSummary {
pub avg_reply_score: f64,
pub avg_tweet_score: f64,
pub total_replies_sent: i64,
pub total_tweets_posted: i64,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct AnalyticsSummary {
pub followers: FollowerSummary,
pub actions_today: ActionsSummary,
pub engagement: EngagementSummary,
pub top_topics: Vec<ContentScore>,
}
pub async fn get_analytics_summary_for(
pool: &DbPool,
account_id: &str,
) -> Result<AnalyticsSummary, StorageError> {
let snapshots = get_follower_snapshots_for(pool, account_id, 90).await?;
let current = snapshots.first().map_or(0, |s| s.follower_count);
let today = Utc::now().date_naive();
let follower_at_or_before = |days: i64| -> i64 {
snapshots
.iter()
.find(|s| {
NaiveDate::parse_from_str(&s.snapshot_date, "%Y-%m-%d")
.map(|d| (today - d).num_days() >= days)
.unwrap_or(false)
})
.map_or(current, |s| s.follower_count)
};
let change_7d = if snapshots.len() >= 2 {
current - follower_at_or_before(7)
} else {
0
};
let change_30d = if snapshots.len() >= 2 {
current - follower_at_or_before(30)
} else {
0
};
let today = Utc::now().format("%Y-%m-%dT00:00:00Z").to_string();
let counts = super::super::action_log::get_action_counts_since(pool, &today).await?;
let actions_today = ActionsSummary {
replies: *counts.get("reply").unwrap_or(&0),
tweets: *counts.get("tweet").unwrap_or(&0),
threads: *counts.get("thread").unwrap_or(&0),
};
let avg_reply_score = get_avg_reply_engagement_for(pool, account_id).await?;
let avg_tweet_score = get_avg_tweet_engagement_for(pool, account_id).await?;
let (total_replies_sent, total_tweets_posted) =
get_performance_counts_for(pool, account_id).await?;
let top_topics = get_top_topics_for(pool, account_id, 5).await?;
Ok(AnalyticsSummary {
followers: FollowerSummary {
current,
change_7d,
change_30d,
},
actions_today,
engagement: EngagementSummary {
avg_reply_score,
avg_tweet_score,
total_replies_sent,
total_tweets_posted,
},
top_topics,
})
}
pub async fn get_analytics_summary(pool: &DbPool) -> Result<AnalyticsSummary, StorageError> {
get_analytics_summary_for(pool, DEFAULT_ACCOUNT_ID).await
}