File size: 12,941 Bytes
5f63f29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
"""

Event Recommendation Scenario Handler

Recommends events based on user's vibe/mood with RAG integration

"""
from typing import Dict, Any
from .base_handler import BaseScenarioHandler


class EventRecommendationHandler(BaseScenarioHandler):
    """

    Handle event recommendation flow

    

    Steps:

    1. Ask for vibe/mood (Chill, Sôi động, Hài, Workshop)

    2. Search events matching vibe → RAG

    3. Show event list, ask which to see details

    4. Ask what info needed (price, lineup, location, time)

    5-8. Show specific info → RAG

    9. Ask if want to save event to email

    10. Collect email + send summary

    11-12. End scenario

    """
    
    def start(self, initial_data: Dict = None) -> Dict[str, Any]:
        """Start event recommendation flow"""
        return {
            "message": "Hello! 👋 Bạn muốn tìm sự kiện theo vibe gì nè? Chill – Sôi động – Hài – Workshop?",
            "new_state": {
                "active_scenario": "event_recommendation",
                "scenario_step": 1,
                "scenario_data": initial_data or {}
            }
        }
    
    def next_step(self, current_step: int, user_input: str, scenario_data: Dict) -> Dict[str, Any]:
        """Process user input and advance scenario"""
        
        # Get expected input type for this step
        expected_type = self._get_expected_type(current_step)
        
        # Check for unexpected input (off-topic questions)
        unexpected = self.handle_unexpected_input(user_input, expected_type, current_step)
        if unexpected:
            return unexpected
        
        # ===== STEP 1: Collect interest tag =====
        if current_step == 1:
            scenario_data['interest_tag'] = user_input
            
            return {
                "message": f"Mình hiểu rồi! Để mình tìm sự kiện hợp vibe **{user_input}** nha",
                "new_state": {
                    "active_scenario": "event_recommendation",
                    "scenario_step": 2,
                    "scenario_data": scenario_data
                },
                "scenario_active": True
            }
        
        # ===== STEP 2: Execute RAG search for events =====
                "new_state": {
                    "active_scenario": "event_recommendation",
                    "scenario_step": 3,
                    "scenario_data": scenario_data
                },
                "scenario_active": True,
                "loading_message": "⏳ Bạn đợi tôi tìm 1 chút nhé..."
            }
        
        # ===== STEP 3: User picks event =====
        elif current_step == 3:
            scenario_data['event_name'] = user_input
            
            return {
                "message": "Bạn cần xem: giá – line-up – địa điểm – hay thời gian của sự kiện?",
                "new_state": {
                    "active_scenario": "event_recommendation",
                    "scenario_step": 4,
                    "scenario_data": scenario_data
                },
                "scenario_active": True
            }
        
        # ===== STEP 4: Branch based on info choice =====
        elif current_step == 4:
            choice = self._detect_choice(user_input)
            event_name = scenario_data.get('event_name', 'sự kiện này')
            
            # Build RAG query based on choice
            query_map = {
                'price': f"giá vé {event_name}",
                'lineup': f"lineup nghệ sĩ {event_name}",
                'location': f"địa điểm tổ chức {event_name}",
                'time': f"thời gian lịch diễn {event_name}"
            }
            
            query = query_map.get(choice, query_map['price'])
            print(f"🔍 RAG Search: {query}")
            
            results = self._search_rag(query)
            formatted_info = self._format_rag_results(results)
            
            # Build response message
            message_map = {
                'price': f"Giá vé event {event_name} nè:\n{formatted_info}",
                'lineup': f"Lineup / nghệ sĩ của event {event_name} là:\n{formatted_info}",
                'location': f"Địa điểm tổ chức event {event_name}:\n{formatted_info}",
                'time': f"Thời gian / lịch diễn của event {event_name}:\n{formatted_info}"
            }
            
            return {
                "message": message_map.get(choice, message_map['price']),
                "new_state": {
                    "active_scenario": "event_recommendation",
                    "scenario_step": 9,  # Skip to email step
                    "scenario_data": scenario_data
                },
                "scenario_active": True,
                "loading_message": "⏳ Bạn đợi tôi tìm 1 chút nhé..."
            }
        
        # ===== STEP 9: Ask if want to save event to email =====
        elif current_step == 9:
            choice = self._detect_yes_no(user_input)
            
            if choice == 'yes':
                return {
                    "message": "Cho mình xin email để gửi bản tóm tắt event kèm link mua vé?",
                    "new_state": {
                        "active_scenario": "event_recommendation",
                        "scenario_step": 10,
                        "scenario_data": scenario_data
                    },
                    "scenario_active": True
                }
            else:
                return {
                    "message": "Okie, bạn cần event theo vibe khác không nè? 😄",
                    "new_state": None,
                    "scenario_active": False,
                    "end_scenario": True
                }
        
        # ===== STEP 10: Collect email and send summary =====
        elif current_step == 10:
            email = user_input.strip()
            
            if not self._validate_email(email):
                return {
                    "message": "Email này có vẻ không đúng định dạng. Bạn nhập lại giúp mình nhé? (Ví dụ: name@example.com)",
                    "new_state": None,  # Stay at same step
                    "scenario_active": True
                }
            
            # Save lead
            scenario_data['email'] = email
            
            try:
                self.lead_storage.save_lead(
                    event_name=scenario_data.get('event_name', 'Unknown Event'),
                    email=email,
                    interests={
                        "vibe": scenario_data.get('interest_tag'),
                        "wants_event_summary": True
                    },
                    session_id=scenario_data.get('session_id')
                )
                print(f"📧 Lead saved: {email}")
            except Exception as e:
                print(f"⚠️ Error saving lead: {e}")
            
            return {
                "message": "Đã gửi email cho bạn nha! ✨",
                "new_state": None,
                "scenario_active": False,
                "end_scenario": True,
                "action": "send_event_summary_email"
            }
        
        # Fallback - unknown step
        return {
            "message": "Xin lỗi, có lỗi xảy ra. Bạn muốn bắt đầu lại không?",
            "new_state": None,
            "scenario_active": False,
            "end_scenario": True
        }
    
    def _get_expected_type(self, step: int) -> str:
        """Get expected input type for each step"""
        type_map = {
            1: 'interest_tag',
            2: None,  # Auto-advance after RAG
            3: 'event_name',
            4: 'choice',
            9: 'choice',
            10: 'email'
        }
        return type_map.get(step, 'text')
    
    def _format_event_list(self, results: list) -> str:
        """Format event search results as numbered list"""
        print(f"🔍 DEBUG: RAG returned {len(results)} results")
        
        if not results or len(results) == 0:
            return "Hiện tại chưa có event phù hợp 😢\nBạn thử vibe khác nhé!"
        
        # Debug: Print first result to see structure
        if len(results) > 0:
            print(f"🔍 DEBUG: First result metadata: {results[0].get('metadata', {})}")
        
        events = []
        for i, r in enumerate(results[:3], 1):
            metadata = r.get('metadata', {})
            
            # Extract event info from metadata
            # Your Qdrant has: {'texts': [...], 'id_use': '...'}
            event_id = metadata.get('id_use', metadata.get('original_id'))
            texts = metadata.get('texts', [])
            text = texts[0] if texts and len(texts) > 0 else metadata.get('text', '')
            
            # Use first 60 chars of text as event name
            name = text[:60].strip() + "..." if len(text) > 60 else text.strip()
            
            print(f"🔍 DEBUG: Event {i}: id={event_id}, name={name[:50]}")
            
            # Simple format for now (can enhance with API call later)
            event_str = f"{i}. **{name}**"
            
            # Store event_id for later API call if needed
            if event_id:
                event_str += f" (ID: {event_id[:8]}...)"
            
            events.append(event_str)
        
        return "\n".join(events)
    
    async def _format_event_list_with_api(self, results: list) -> str:
        """

        Format event search results by calling API for full details

        """
        print(f"🔍 DEBUG: RAG returned {len(results)} results")
        
        if not results or len(results) == 0:
            return "Hiện tại chưa có event phù hợp 😢\nBạn thử vibe khác nhé!"
        
        # Import event service
        from event_service import EventService
        event_service = EventService()
        
        events = []
        for i, r in enumerate(results[:3], 1):
            metadata = r.get('metadata', {})
            event_id = metadata.get('id_use', metadata.get('original_id'))
            
            print(f"🔍 DEBUG: Fetching event {i} with ID: {event_id}")
            
            # Try to get full event data from API
            event_data = None
            if event_id:
                event_data = await event_service.get_event_by_id(event_id)
            
            if event_data:
                # Use API data
                name = event_data.get("eventName", "Sự kiện")
                start = event_data.get("eventStartTime", "")
                date_str = start[:10] if start else "TBA"
                location = event_data.get("eventAddress", "")
                
                event_str = f"{i}. **{name}**"
                if date_str != "TBA":
                    event_str += f" ({date_str})"
                if location:
                    event_str += f" - {location}"
                
                print(f"✅ Event {i}: {name} ({date_str})")
            else:
                # Fallback to text from Qdrant
                texts = metadata.get('texts', [])
                text = texts[0] if texts and len(texts) > 0 else ""
                name = text[:60].strip() + "..." if len(text) > 60 else text.strip()
                
                event_str = f"{i}. **{name}**"
                print(f"⚠️ Event {i}: Fallback to text (API failed)")
            
            events.append(event_str)
        
        await event_service.close()
        return "\n".join(events)
    
    def _detect_choice(self, user_input: str) -> str:
        """Detect what info user wants to see"""
        input_lower = user_input.lower()
        
        if any(k in input_lower for k in ['giá', 'price', 'vé', 'ticket', 'bao nhiêu']):
            return 'price'
        elif any(k in input_lower for k in ['lineup', 'line-up', 'nghệ sĩ', 'artist', 'performer']):
            return 'lineup'
        elif any(k in input_lower for k in ['địa điểm', 'location', 'ở đâu', 'where', 'chỗ']):
            return 'location'
        elif any(k in input_lower for k in ['thời gian', 'time', 'khi nào', 'when', 'lịch', 'date']):
            return 'time'
        else:
            return 'price'  # Default
    
    def _detect_yes_no(self, user_input: str) -> str:
        """Detect yes/no response"""
        input_lower = user_input.lower()
        
        if any(k in input_lower for k in ['có', 'yes', 'ok', 'được', 'ừ', 'oke']):
            return 'yes'
        else:
            return 'no'