1#![allow(non_snake_case)]
4
5use super::*;
6
7use std::path::Path;
8
9pub fn list_agreements() -> Result<serde_json::Value, String> {
11 serde_json::to_value(crate::agreements::list_agreements()).map_err(|e| e.to_string())
12}
13
14pub fn agreements_for(did: String) -> Result<serde_json::Value, String> {
16 serde_json::to_value(crate::agreements::agreements_for(&did)).map_err(|e| e.to_string())
17}
18
19pub fn create_agreement(
22 title: String,
23 relationship_did: String,
24 parties: Vec<String>,
25) -> Result<serde_json::Value, String> {
26 let now = mail_now_unix();
27 let a = crate::agreements::Agreement {
28 id: uuid::Uuid::new_v4().to_string(),
29 title,
30 relationship_did,
31 parties: parties.clone(),
32 values_anchors: vec!["urn:qualia:values:udhr".to_string()],
33 undertakings: vec![],
34 consents: parties
35 .into_iter()
36 .map(|did| crate::agreements::PartyConsent {
37 did,
38 consent: crate::agreements::ConsentState::Pending,
39 signature_hex: None,
40 })
41 .collect(),
42 stage: crate::agreements::FormationStage::Draft,
43 jurisdiction: None,
44 intents: Vec::new(),
45 artifact_context: None,
46 created_at: now,
47 updated_at: now,
48 };
49 crate::agreements::upsert_agreement(a.clone())?;
50 serde_json::to_value(a).map_err(|e| e.to_string())
51}
52
53pub fn save_agreement(agreement_json: String) -> Result<serde_json::Value, String> {
55 let a: crate::agreements::Agreement =
56 serde_json::from_str(&agreement_json).map_err(|e| format!("bad agreement: {e}"))?;
57 crate::agreements::upsert_agreement(a)?;
58 list_agreements()
59}
60
61pub fn set_agreement_consent(
63 id: String,
64 did: String,
65 state: String,
66) -> Result<serde_json::Value, String> {
67 let mut all = crate::agreements::list_agreements();
68 let a = all
69 .iter_mut()
70 .find(|a| a.id == id)
71 .ok_or_else(|| format!("unknown agreement '{id}'"))?;
72 let cs = match state.to_lowercase().as_str() {
73 "granted" => crate::agreements::ConsentState::Granted,
74 "withdrawn" => crate::agreements::ConsentState::Withdrawn,
75 _ => crate::agreements::ConsentState::Pending,
76 };
77 crate::agreements::set_consent(a, &did, cs);
78 a.updated_at = mail_now_unix();
79 let updated = a.clone();
80 crate::agreements::upsert_agreement(updated)?;
81 list_agreements()
82}
83
84pub fn get_chat_graph(session_id: String) -> Result<serde_json::Value, String> {
85 let state = crate::state::APP_STATE.get().unwrap();
86 let storage = state.config.lock().unwrap().storage_path.clone();
87 let graph = crate::chat_graph::load_graph(Path::new(&storage), &session_id)
88 .map_err(|e| e.to_string())?;
89 let session = crate::chat_session::load_session(Path::new(&storage), &session_id)
90 .map_err(|e| e.to_string())?;
91 let reactions =
92 crate::chat_ontology::list_reactions(Path::new(&storage), &session_id).unwrap_or_default();
93 let branch_types = crate::chat_ontology::list_branch_types(Path::new(&storage));
94 serde_json::to_value(serde_json::json!({
95 "fragments": graph.fragments,
96 "edges": graph.edges,
97 "messages": session.messages,
98 "reactions": reactions,
99 "branch_types": branch_types,
100 "wordnet": crate::chat_ontology::resolve_wordnet_q42(Path::new(&storage))
101 .map(|p| p.to_string_lossy().to_string()),
102 }))
103 .map_err(|e| e.to_string())
104}
105
106pub fn create_chat_fragment(
107 session_id: String,
108 message_lamport: u64,
109 anchor_start: u32,
110 anchor_end: u32,
111) -> Result<serde_json::Value, String> {
112 let state = crate::state::APP_STATE.get().unwrap();
113 let storage = state.config.lock().unwrap().storage_path.clone();
114 let session = crate::chat_session::load_session(Path::new(&storage), &session_id)
115 .map_err(|e| e.to_string())?;
116 let msg = session
117 .messages
118 .iter()
119 .find(|m| m.lamport == message_lamport)
120 .ok_or_else(|| format!("message {message_lamport} not found"))?;
121 let fragment = crate::chat_graph::create_fragment_from_selection(
122 Path::new(&storage),
123 &session_id,
124 message_lamport,
125 &msg.content,
126 anchor_start,
127 anchor_end,
128 )
129 .map_err(|e| e.to_string())?;
130 serde_json::to_value(fragment).map_err(|e| e.to_string())
131}
132
133pub fn sync_chat_relay(session_id: Option<String>) -> Result<u64, String> {
134 let state = crate::state::APP_STATE.get().unwrap();
135 let storage = state.config.lock().unwrap().storage_path.clone();
136 if let Some(id) = session_id {
137 Ok(crate::chat_relay::sync_session_relay(Path::new(&storage), &id)? as u64)
138 } else {
139 Ok(crate::chat_relay::sync_all_group_sessions()? as u64)
140 }
141}
142
143pub fn start_chat_relay_poller() {
144 crate::chat_relay::start_relay_poller();
145}
146
147pub fn list_chat_branch_types() -> Result<serde_json::Value, String> {
148 let state = crate::state::APP_STATE.get().unwrap();
149 let storage = state.config.lock().unwrap().storage_path.clone();
150 let types = crate::chat_ontology::list_branch_types(Path::new(&storage));
151 serde_json::to_value(types).map_err(|e| e.to_string())
152}
153
154pub fn classify_chat_branch(
155 anchor_text: String,
156 reply_text: String,
157) -> Result<serde_json::Value, String> {
158 let state = crate::state::APP_STATE.get().unwrap();
159 let storage = state.config.lock().unwrap().storage_path.clone();
160 let c = crate::chat_ontology::classify_branch(Path::new(&storage), &anchor_text, &reply_text);
161 serde_json::to_value(c).map_err(|e| e.to_string())
162}
163
164pub fn toggle_chat_reaction(
165 session_id: String,
166 message_lamport: u64,
167 emoji: String,
168) -> Result<serde_json::Value, String> {
169 let state = crate::state::APP_STATE.get().unwrap();
170 let storage = state.config.lock().unwrap().storage_path.clone();
171 let reactions = crate::chat_ontology::toggle_reaction(
172 Path::new(&storage),
173 &session_id,
174 message_lamport,
175 &emoji,
176 )?;
177 serde_json::to_value(reactions).map_err(|e| e.to_string())
178}
179
180pub fn list_chat_reactions(session_id: String) -> Result<serde_json::Value, String> {
181 let state = crate::state::APP_STATE.get().unwrap();
182 let storage = state.config.lock().unwrap().storage_path.clone();
183 let reactions = crate::chat_ontology::list_reactions(Path::new(&storage), &session_id)?;
184 serde_json::to_value(reactions).map_err(|e| e.to_string())
185}
186
187pub fn wordnet_chat_ontology_status() -> Result<serde_json::Value, String> {
188 let state = crate::state::APP_STATE.get().unwrap();
189 let storage = state.config.lock().unwrap().storage_path.clone();
190 let path = crate::chat_ontology::resolve_wordnet_q42(Path::new(&storage));
191 Ok(serde_json::json!({
192 "available": path.is_some(),
193 "q42_path": path.as_ref().map(|p| p.to_string_lossy().to_string()),
194 "lex_path": path.as_ref().and_then(|p| crate::chat_ontology::resolve_wordnet_lex(p).map(|l| l.to_string_lossy().to_string())),
195 }))
196}
197
198pub fn default_chat_file_sharing(session_id: String) -> Result<serde_json::Value, String> {
199 let state = crate::state::APP_STATE.get().unwrap();
200 let storage = state.config.lock().unwrap().storage_path.clone();
201 let session = crate::chat_session::load_session(Path::new(&storage), &session_id)
202 .map_err(|e| e.to_string())?;
203 let sharing = crate::chat_files::default_sharing_for_session(session.meta.session_kind);
204 serde_json::to_value(sharing).map_err(|e| e.to_string())
205}
206
207pub fn attach_chat_file(
208 session_id: String,
209 source_path: String,
210 sharing_json: serde_json::Value,
211) -> Result<serde_json::Value, String> {
212 let state = crate::state::APP_STATE.get().unwrap();
213 let storage = state.config.lock().unwrap().storage_path.clone();
214 let sharing: crate::chat_files::ChatFileSharing =
215 serde_json::from_value(sharing_json).map_err(|e| e.to_string())?;
216 let result = crate::chat_files::attach_chat_file(
217 Path::new(&storage),
218 &session_id,
219 Path::new(&source_path),
220 sharing,
221 )?;
222 serde_json::to_value(result).map_err(|e| e.to_string())
223}
224
225pub fn list_chat_files(session_id: String) -> Result<serde_json::Value, String> {
226 let state = crate::state::APP_STATE.get().unwrap();
227 let storage = state.config.lock().unwrap().storage_path.clone();
228 let files = crate::chat_files::list_chat_files(Path::new(&storage), &session_id, None)?;
229 serde_json::to_value(files).map_err(|e| e.to_string())
230}
231
232pub fn set_chat_file_sharing(
233 session_id: String,
234 file_id: String,
235 sharing_json: serde_json::Value,
236) -> Result<serde_json::Value, String> {
237 let state = crate::state::APP_STATE.get().unwrap();
238 let storage = state.config.lock().unwrap().storage_path.clone();
239 let sharing: crate::chat_files::ChatFileSharing =
240 serde_json::from_value(sharing_json).map_err(|e| e.to_string())?;
241 let updated = crate::chat_files::set_chat_file_sharing(
242 Path::new(&storage),
243 &session_id,
244 &file_id,
245 sharing,
246 )?;
247 serde_json::to_value(updated).map_err(|e| e.to_string())
248}
249
250pub fn get_chat_file_local_path(
251 session_id: String,
252 file_id: String,
253 variant: String,
254) -> Result<String, String> {
255 let state = crate::state::APP_STATE.get().unwrap();
256 let storage = state.config.lock().unwrap().storage_path.clone();
257 let path = crate::chat_files::resolve_chat_file_path(
258 Path::new(&storage),
259 &session_id,
260 &file_id,
261 &variant,
262 None,
263 )?;
264 Ok(path.to_string_lossy().to_string())
265}
266
267pub fn parse_chat_file_preview(source_path: String) -> Result<serde_json::Value, String> {
268 let path = Path::new(&source_path);
269 if !path.is_file() {
270 return Err(format!("File not found: {}", path.display()));
271 }
272 let mut bytes = Vec::new();
273 std::fs::File::open(path)
274 .and_then(|mut f| std::io::Read::read_to_end(&mut f, &mut bytes))
275 .map_err(|e| e.to_string())?;
276 let name = path.file_name().and_then(|s| s.to_str()).unwrap_or("file");
277 let parsed = crate::chat_files::parse_document_bytes(name, &bytes);
278 serde_json::to_value(parsed).map_err(|e| e.to_string())
279}