1+ using System . Collections . ObjectModel ;
2+ using AutoMapper ;
3+ using LudereAI . Core . Entities . Chat ;
4+ using LudereAI . Core . Interfaces . Services ;
5+ using LudereAI . Shared . Enums ;
6+ using LudereAI . Shared . Models ;
7+ using LudereAI . WPF . Interfaces ;
8+ using LudereAI . WPF . Models ;
9+ using Microsoft . Extensions . Logging ;
10+
11+ namespace LudereAI . WPF . Services ;
12+
13+ public class ChatCoordinator : IChatCoordinator
14+ {
15+ private readonly ILogger < ChatCoordinator > _logger ;
16+ private readonly IChatService _chat ;
17+ private readonly IAudioService _audio ;
18+ private readonly IMapper _mapper ;
19+ private readonly IChatSession _session ;
20+
21+ public ChatCoordinator ( ILogger < ChatCoordinator > logger ,
22+ IChatService chat ,
23+ IAudioService audio ,
24+ IMapper mapper ,
25+ IChatSession session )
26+ {
27+ _logger = logger ;
28+ _chat = chat ;
29+ _audio = audio ;
30+ _mapper = mapper ;
31+ _session = session ;
32+ }
33+
34+ public async Task Initialize ( ) => await RefreshConversations ( ) ;
35+
36+ public bool CanSend ( string message , ConversationModel ? conversation , string ? gameContext )
37+ => ! string . IsNullOrWhiteSpace ( message )
38+ && ! _session . IsAssistantThinking
39+ && conversation != null
40+ && ! string . IsNullOrWhiteSpace ( gameContext ) ;
41+
42+ public async Task RefreshConversations ( )
43+ {
44+ try
45+ {
46+ var conversations = await _chat . GetConversations ( ) ;
47+ var mappedConversations = _mapper . Map < IEnumerable < ConversationModel > > ( conversations ) . ToList ( ) ;
48+ foreach ( var m in mappedConversations . SelectMany ( c => c . Messages ) )
49+ m . CreatedAt = m . CreatedAt . ToLocalTime ( ) ;
50+
51+ _session . SetConversations ( new ObservableCollection < ConversationModel > ( mappedConversations ) ) ;
52+ }
53+ catch ( Exception ex )
54+ {
55+ _logger . LogError ( ex , "Failed to refresh conversations" ) ;
56+ }
57+ }
58+
59+ public async Task DeleteConversation ( ConversationModel conversation )
60+ {
61+ try
62+ {
63+ var success = await _chat . DeleteConversation ( conversation . Id ) ;
64+ if ( success )
65+ {
66+ _session . Conversations . Remove ( conversation ) ;
67+ if ( _session . CurrentConversation == conversation )
68+ {
69+ _session . CurrentConversation = _session . Conversations . FirstOrDefault ( ) ;
70+ }
71+
72+ await RefreshConversations ( ) ;
73+ }
74+ else
75+ {
76+ _logger . LogWarning ( "Failed to delete conversation with ID {ConversationId}" , conversation . Id ) ;
77+ }
78+ }
79+ catch ( Exception ex )
80+ {
81+ _logger . LogError ( ex , "Failed to delete conversation" ) ;
82+ }
83+ }
84+
85+ public async Task SendMessage ( string message , string ? gameContext , WindowInfo ? windowContext )
86+ {
87+ if ( _session . CurrentConversation == null ) return ;
88+
89+ try
90+ {
91+ _session . IsAssistantThinking = true ;
92+ _session . AddMessageLocal ( message , MessageRole . User ) ;
93+
94+ var result = await _chat . SendMessage ( new ChatRequest
95+ {
96+ Message = message ,
97+ ConversationId = _session . CurrentConversation . Id ,
98+ GameContext = gameContext ,
99+ Window = windowContext
100+ } ) ;
101+
102+ if ( result is { IsSuccessful : true , Value : not null } )
103+ {
104+ UpdateConversationState ( result . Value ) ;
105+ var lastAssistantMessage =
106+ _session . CurrentConversation . Messages . LastOrDefault ( m => m . Role == MessageRole . Assistant ) ;
107+ if ( lastAssistantMessage ? . Audio . Length > 0 )
108+ {
109+ await _audio . PlayAudioAsync ( lastAssistantMessage . Audio ) ;
110+ }
111+ }
112+ else
113+ {
114+ _session . AddSystemMessage ( result . Message ) ;
115+ }
116+ }
117+ catch ( Exception ex )
118+ {
119+ _logger . LogError ( ex , "Failed to send message" ) ;
120+ _session . AddSystemMessage ( "An error occurred while sending your message. Please try again." ) ;
121+ }
122+ finally
123+ {
124+ _session . IsAssistantThinking = false ;
125+ }
126+ }
127+
128+ private void UpdateConversationState ( Conversation conversation )
129+ {
130+ var updated = _mapper . Map < ConversationModel > ( conversation ) ;
131+ foreach ( var m in updated . Messages ) m . CreatedAt = m . CreatedAt . ToLocalTime ( ) ;
132+
133+ var existing = _session . Conversations . FirstOrDefault ( c => c . Id == updated . Id ) ;
134+ if ( existing != null )
135+ {
136+ var index = _session . Conversations . IndexOf ( existing ) ;
137+ _session . Conversations [ index ] = updated ;
138+ }
139+ else
140+ {
141+ _session . Conversations . Insert ( 0 , updated ) ;
142+ }
143+
144+ _session . CurrentConversation = updated ;
145+ _session . SetMessages ( new ObservableCollection < MessageModel > ( updated . Messages ) ) ;
146+ }
147+ }
0 commit comments