use std::sync::Arc;
use crate::config::Config;
use crate::content::frameworks::ThreadStructure;
use crate::llm::LlmProvider;
use super::{make_content_gen, WorkflowError};
#[derive(Debug, Clone)]
pub struct ThreadPlanInput {
pub topic: String,
pub objective: Option<String>,
pub target_audience: Option<String>,
pub structure: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct ThreadPlanOutput {
pub thread_tweets: Vec<String>,
pub tweet_count: usize,
pub structure_used: String,
pub hook_type: String,
pub first_tweet_preview: String,
pub estimated_performance: String,
pub objective_alignment: String,
pub target_audience: String,
pub topic_relevance: String,
}
fn parse_structure(s: &str) -> Option<ThreadStructure> {
match s.to_lowercase().as_str() {
"transformation" => Some(ThreadStructure::Transformation),
"framework" => Some(ThreadStructure::Framework),
"mistakes" => Some(ThreadStructure::Mistakes),
"analysis" => Some(ThreadStructure::Analysis),
_ => None,
}
}
fn analyze_hook(first_tweet: &str) -> &'static str {
let trimmed = first_tweet.trim();
if trimmed.ends_with('?') {
"question"
} else if trimmed.starts_with("Most people")
|| trimmed.starts_with("Everyone")
|| trimmed.starts_with("most people")
{
"contrarian"
} else if trimmed.starts_with("I ") || trimmed.starts_with("I'") {
"story"
} else {
"statement"
}
}
pub async fn execute(
llm: &Arc<dyn LlmProvider>,
config: &Config,
input: ThreadPlanInput,
) -> Result<ThreadPlanOutput, WorkflowError> {
let structure_override = input.structure.as_deref().and_then(parse_structure);
let gen = make_content_gen(llm, &config.business);
let thread = gen
.generate_thread_with_structure(&input.topic, structure_override)
.await?;
let tweet_count = thread.tweets.len();
let hook_type = thread
.tweets
.first()
.map(|t| analyze_hook(t))
.unwrap_or("unknown");
let topic_lower = input.topic.to_lowercase();
let relevance = config.business.effective_industry_topics().iter().any(|t| {
topic_lower.contains(&t.to_lowercase()) || t.to_lowercase().contains(&topic_lower)
});
let estimated_performance = if relevance { "high" } else { "medium" };
let structure_used = input.structure.as_deref().unwrap_or("auto_selected");
Ok(ThreadPlanOutput {
first_tweet_preview: thread.tweets.first().cloned().unwrap_or_default(),
thread_tweets: thread.tweets,
tweet_count,
structure_used: structure_used.to_string(),
hook_type: hook_type.to_string(),
estimated_performance: estimated_performance.to_string(),
objective_alignment: input
.objective
.unwrap_or_else(|| "general engagement".to_string()),
target_audience: input
.target_audience
.unwrap_or_else(|| "general".to_string()),
topic_relevance: if relevance {
"matches_industry_topics"
} else {
"novel_topic"
}
.to_string(),
})
}