1use crate::config::{home_config_path, Config, ConfigError};
2use crate::openrouter::{ApiError, Client};
3use crate::ui;
4use colored::Colorize;
5
6fn print_splash_banner() {
7 let banner = r#"
8 ▄ ▄▄▄▄
9 ▀██████▀ ▄███████▄
10 ██ ▄ ▄ ██ ▀█▄ ▀█
11 ██ ▄███▄ ███▄███▄ ███▄███▄ ██ ▄█▀██ ██
12 ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ▄█
13 ▀█████▄▀███▀▄██ ██ ▀█▄██ ██ ▀█ ▀█▄ ▀▀▀▀▀▀
14 ▀██████▀▀
15"#;
16 println!("{}", banner.cyan().bold());
17}
18
19fn prompt_api_key(is_first_run: bool, existing_key: Option<&str>) -> String {
20 loop {
21 let mut prompt_text = "Enter OpenRouter API Key (sk-or-v1-...):".to_string();
22 if !is_first_run {
23 prompt_text.push_str(" (Leave blank to keep existing)");
24 }
25
26 let password = inquire::Password::new(&prompt_text)
27 .with_display_mode(inquire::PasswordDisplayMode::Masked)
28 .with_help_message("API key at https://openrouter.ai/keys")
29 .prompt()
30 .expect("User cancelled");
31
32 if password.is_empty() {
33 if is_first_run {
34 ui::error_message("API key is required on first run. Please enter your key.");
35 continue;
36 } else if let Some(key) = existing_key {
37 return key.to_string();
38 } else {
39 ui::error_message("No existing API key found. Please enter your key.");
40 continue;
41 }
42 }
43 break password;
44 }
45}
46
47pub fn validate_max_chars_input(input: &str) -> Result<usize, String> {
48 if input.is_empty() {
49 return Ok(15_000);
50 }
51 match input.parse::<usize>() {
52 Ok(0) => Err("Must be at least 1".to_string()),
53 Ok(n) => Ok(n),
54 Err(_) => Err(format!("'{}' is not a valid number", input)),
55 }
56}
57
58pub fn run_setup_flow(is_first_run: bool) -> Result<Config, ConfigError> {
59 print_splash_banner();
60
61 let existing_key = if !is_first_run {
62 home_config_path()
63 .ok()
64 .and_then(|path| Config::load_from_path(&path).ok())
65 .map(|c| c.api_key)
66 } else {
67 None
68 };
69
70 let api_key = prompt_api_key(is_first_run, existing_key.as_deref());
71 let mut client = Client::new(api_key.clone());
72
73 let models = loop {
74 ui::fetching_models_message();
75 match client.fetch_models() {
76 Ok(m) => break m,
77 Err(ApiError::Unauthorized) => {
78 ui::error_message("Invalid API Key. Make sure you entered the correct key.");
79 let new_key = prompt_api_key(true, None);
80 client = Client::new(new_key);
81 continue;
82 }
83 Err(ApiError::RateLimited) => {
84 ui::rate_limited_message();
85 std::thread::sleep(std::time::Duration::from_secs(2));
86 continue;
87 }
88 Err(ApiError::EmptyResponse) => {
89 ui::error_message("No models available. Please try again.");
90 continue;
91 }
92 Err(ApiError::Forbidden) => {
93 return Err(ConfigError::ApiError(
94 "API key doesn't have access. Check permissions on OpenRouter.".into(),
95 ));
96 }
97 Err(e) => {
98 return Err(ConfigError::ApiError(format!(
99 "Failed to fetch models: {}. Please try again.",
100 e
101 )));
102 }
103 }
104 };
105
106 let model_ids: Vec<String> = models.iter().map(|m| m.id.clone()).collect();
107 ui::models_loaded(model_ids.len());
108
109 let selection = ui::model_select_prompt(&model_ids);
110 let model_id = if selection == "[ Type Manual Model ID... ]" {
111 ui::manual_model_prompt()
112 } else {
113 selection
114 };
115
116 let current_max_chars = if !is_first_run {
117 home_config_path()
118 .ok()
119 .and_then(|path| Config::load_from_path(&path).ok())
120 .map(|c| c.max_chars)
121 .unwrap_or(15_000)
122 } else {
123 15_000
124 };
125
126 let max_chars = loop {
127 let input = inquire::Text::new(&format!(
128 "Max characters for diff (current: {}):",
129 current_max_chars
130 ))
131 .with_default("")
132 .prompt()
133 .expect("User cancelled");
134
135 match validate_max_chars_input(&input) {
136 Ok(n) => break n,
137 Err(msg) => {
138 ui::error_message(&msg);
139 continue;
140 }
141 }
142 };
143
144 let config = Config {
145 api_key,
146 model_id,
147 max_chars,
148 };
149
150 let path = home_config_path()?;
151 config.save(&path)?;
152
153 ui::save_confirmation();
154 Ok(config)
155}