File size: 28,522 Bytes
d86c812
 
2a42ccf
 
d86c812
 
 
 
 
 
 
 
2662b83
 
 
 
2a42ccf
 
 
d86c812
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
abfa67a
 
 
 
 
d86c812
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2662b83
d86c812
 
2662b83
 
d86c812
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2662b83
 
 
d86c812
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2662b83
 
 
 
 
 
 
 
 
 
 
 
d86c812
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2a42ccf
 
d86c812
2a42ccf
 
2662b83
d86c812
2a42ccf
 
d86c812
2a42ccf
d86c812
2a42ccf
 
 
 
d86c812
 
 
 
2662b83
 
 
 
 
 
 
 
d86c812
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2a42ccf
d86c812
 
 
 
 
 
2a42ccf
d86c812
2a42ccf
 
d86c812
 
2a42ccf
 
 
 
 
 
 
d86c812
 
 
 
 
2a42ccf
d86c812
 
 
 
 
 
 
 
 
2a42ccf
d86c812
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2a42ccf
d86c812
 
 
 
 
 
2a42ccf
d86c812
2a42ccf
 
d86c812
 
2a42ccf
 
 
 
 
d86c812
2662b83
 
 
 
 
 
 
 
d86c812
 
 
 
2a42ccf
 
d86c812
 
 
 
 
 
 
 
 
 
 
2a42ccf
 
d86c812
 
 
 
2662b83
d86c812
 
 
 
 
2a42ccf
 
d86c812
 
 
2a42ccf
 
d86c812
2a42ccf
d86c812
 
 
 
 
 
 
 
2662b83
 
 
 
 
 
 
 
 
d86c812
 
 
 
2a42ccf
 
d86c812
 
 
 
 
 
 
 
2662b83
d86c812
2662b83
 
 
d86c812
2662b83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d86c812
2662b83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d86c812
2662b83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d86c812
2662b83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2a42ccf
 
 
2662b83
2a42ccf
 
2662b83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2a42ccf
2662b83
 
 
 
 
 
 
d86c812
2662b83
 
abfa67a
2662b83
abfa67a
d86c812
 
 
 
 
abfa67a
2a42ccf
 
abfa67a
d86c812
2662b83
 
 
 
 
d86c812
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
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
#!/usr/bin/env python3
"""
πŸš— JAY'S MOBILE WASH - GRADIO + FASTAPI VERSION
iPhone Forwarding + AI Assistant - HuggingFace Spaces Optimized
"""

import os
import json
import requests
import logging
import threading
import time
from datetime import datetime, timedelta
from collections import defaultdict
from threading import Lock
import gradio as gr
from fastapi import FastAPI, Request, Form
from fastapi.responses import PlainTextResponse
import uvicorn

# Load environment variables
from dotenv import load_dotenv
load_dotenv()

# Configuration
SIGNALWIRE_PROJECT_ID = os.environ.get('SIGNALWIRE_PROJECT_ID', '')
SIGNALWIRE_AUTH_TOKEN = os.environ.get('SIGNALWIRE_AUTH_TOKEN', '')
SIGNALWIRE_SPACE_URL = os.environ.get('SIGNALWIRE_SPACE_URL', '')
DEEPSEEK_API_KEY = os.environ.get('DEEPSEEK_API_KEY', '')

# Business settings
BUSINESS_NAME = "Jay's Mobile Wash"
JAY_PHONE = os.environ.get('JAY_PHONE', '+15622289429')
SIGNALWIRE_PHONE = os.environ.get('SIGNALWIRE_PHONE', '+17149278841')
FORWARDING_ENABLED = True
FORWARDING_DELAY = 20

# Services and pricing
SERVICES = {
    'basic_wash': {'price': 25.00, 'name': 'Basic Wash', 'description': 'Exterior wash and dry'},
    'premium_wash': {'price': 45.00, 'name': 'Premium Wash', 'description': 'Wash, wax, interior vacuum'},
    'full_detail': {'price': 85.00, 'name': 'Full Detail', 'description': 'Complete interior & exterior'},
    'ceramic_coating': {'price': 150.00, 'name': 'Ceramic Coating', 'description': 'Premium protection'},
    'headlight_restoration': {'price': 35.00, 'name': 'Headlight Restoration', 'description': 'Clear headlight restoration'}
}

# Initialize SignalWire
signalwire_client = None
VoiceResponse = None
MessagingResponse = None

try:
    from signalwire.rest import Client
    from signalwire.voice_response import VoiceResponse
    from signalwire.messaging_response import MessagingResponse
    
    if SIGNALWIRE_PROJECT_ID and SIGNALWIRE_AUTH_TOKEN and SIGNALWIRE_SPACE_URL:
        signalwire_client = Client(
            SIGNALWIRE_PROJECT_ID,
            SIGNALWIRE_AUTH_TOKEN,
            signalwire_space_url=SIGNALWIRE_SPACE_URL
        )
        print("βœ… SignalWire initialized")
    else:
        print("⚠️ SignalWire credentials not configured")
except ImportError:
    print("⚠️ SignalWire library not available")
except Exception as e:
    print(f"⚠️ SignalWire error: {e}")

# Logging setup
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Thread-safe system state
class SystemState:
    def __init__(self):
        self._lock = Lock()
        self._data = {
            'calls_today': 0,
            'sms_today': 0,
            'calls_forwarded': 0,
            'ai_responses': 0,
            'revenue_today': 0.0,
            'active_calls': {},
            'active_sms': {},
            'start_time': datetime.now(),
            'status': 'healthy',
            'call_log': [],
            'sms_log': []
        }
    
    def get(self, key):
        with self._lock:
            return self._data.get(key, 0)
    
    def set(self, key, value):
        with self._lock:
            self._data[key] = value
    
    def increment(self, key):
        with self._lock:
            self._data[key] = self._data.get(key, 0) + 1
    
    def get_all(self):
        with self._lock:
            return self._data.copy()
    
    def add_call_log(self, entry):
        with self._lock:
            self._data['call_log'].insert(0, entry)
            if len(self._data['call_log']) > 50:
                self._data['call_log'] = self._data['call_log'][:50]
    
    def add_sms_log(self, entry):
        with self._lock:
            self._data['sms_log'].insert(0, entry)
            if len(self._data['sms_log']) > 50:
                self._data['sms_log'] = self._data['sms_log'][:50]

# Initialize system state
system_state = SystemState()

# Simple AI processor
class SimpleAI:
    def __init__(self):
        self.response_cache = {}
        
    def analyze_sentiment(self, text):
        """Simple sentiment analysis"""
        try:
            from textblob import TextBlob
            blob = TextBlob(text)
            polarity = blob.sentiment.polarity
            return {
                'score': polarity,
                'label': 'positive' if polarity > 0.1 else 'negative' if polarity < -0.1 else 'neutral'
            }
        except:
            return {'score': 0.0, 'label': 'neutral'}
    
    def detect_intent(self, text):
        """Detect customer intent"""
        text_lower = text.lower()
        
        if any(word in text_lower for word in ['price', 'cost', 'how much', 'pricing']):
            return 'pricing'
        elif any(word in text_lower for word in ['book', 'schedule', 'appointment', 'when']):
            return 'booking'
        elif any(word in text_lower for word in ['service', 'wash', 'detail', 'clean']):
            return 'services'
        elif any(word in text_lower for word in ['location', 'where', 'area', 'address']):
            return 'location'
        elif any(word in text_lower for word in ['urgent', 'emergency', 'asap', 'now']):
            return 'urgent'
        elif any(word in text_lower for word in ['jay', 'owner', 'human', 'person']):
            return 'human_request'
        else:
            return 'general'
    
    def generate_response(self, user_input, context=None):
        """Generate AI response"""
        intent = self.detect_intent(user_input)
        sentiment = self.analyze_sentiment(user_input)
        
        # Add forwarding acknowledgment if needed
        forwarding_ack = ""
        if context and context.get('forwarded'):
            forwarding_ack = "Thank you for your patience while the call was connected. "
        
        # Generate response based on intent
        if intent == 'pricing':
            response = f"{forwarding_ack}I'd be happy to help with pricing! Our basic wash is $25, premium wash is $45, and full detail is $85. Which service interests you most?"
        
        elif intent == 'booking':
            response = f"{forwarding_ack}I can help you schedule an appointment! We're available Monday-Saturday 8AM-6PM, Sunday 10AM-4PM. What day works best for you?"
        
        elif intent == 'services':
            response = f"{forwarding_ack}We offer mobile car detailing services: Basic wash ($25), Premium wash with wax ($45), Full detail ($85), and ceramic coating ($150). Which would you like to know more about?"
        
        elif intent == 'location':
            response = f"{forwarding_ack}We provide mobile service within a 15-mile radius of Los Angeles. We come to your location! Where are you located?"
        
        elif intent == 'urgent':
            response = f"{forwarding_ack}I understand this is urgent. Let me connect you with Jay directly right away for immediate assistance."
        
        elif intent == 'human_request':
            response = f"{forwarding_ack}Let me connect you with Jay personally. He'll be right with you!"
        
        else:
            response = f"{forwarding_ack}Hi! Thanks for contacting Jay's Mobile Wash! I can help with pricing, scheduling, or questions about our mobile car detailing services. How can I assist you today?"
        
        # Try DeepSeek if available for better responses
        if DEEPSEEK_API_KEY and intent not in ['urgent', 'human_request']:
            try:
                deepseek_response = self.get_deepseek_response(user_input, context)
                if deepseek_response:
                    response = deepseek_response
            except Exception as e:
                logger.warning(f"DeepSeek failed: {e}")
        
        return response
    
    def get_deepseek_response(self, prompt, context=None):
        """Get response from DeepSeek API"""
        try:
            headers = {
                "Authorization": f"Bearer {DEEPSEEK_API_KEY}",
                "Content-Type": "application/json"
            }
            
            system_prompt = f"""You are Jay's Mobile Wash AI assistant. Be friendly and professional.

BUSINESS INFO:
- Services: Basic wash ($25), Premium wash ($45), Full detail ($85), Ceramic coating ($150), Headlight restoration ($35)
- Hours: Mon-Sat 8AM-6PM, Sun 10AM-4PM  
- Service area: 15 mile radius from Los Angeles
- Owner: Jay, Phone: {JAY_PHONE}

{"IMPORTANT: Customer's call was forwarded from Jay's phone. Be empathetic about any wait time." if context and context.get('forwarded') else ""}"""
            
            data = {
                "model": "deepseek-chat",
                "messages": [
                    {"role": "system", "content": system_prompt},
                    {"role": "user", "content": prompt}
                ],
                "max_tokens": 150,
                "temperature": 0.7
            }
            
            response = requests.post(
                "https://api.deepseek.com/v1/chat/completions",
                headers=headers,
                json=data,
                timeout=10
            )
            
            if response.status_code == 200:
                result = response.json()
                return result['choices'][0]['message']['content'].strip()
                
        except Exception as e:
            logger.error(f"DeepSeek error: {e}")
        
        return None

# Initialize AI processor
ai = SimpleAI()

# FastAPI app for webhooks
fastapi_app = FastAPI()

@fastapi_app.post("/voice/incoming")
async def handle_voice(request: Request):
    """Handle incoming voice calls"""
    try:
        form = await request.form()
        
        if not signalwire_client or not VoiceResponse:
            return PlainTextResponse("Service unavailable", status_code=503)
        
        call_sid = form.get('CallSid')
        from_number = form.get('From')
        to_number = form.get('To')
        forwarded_from = form.get('ForwardedFrom')
        
        # Detect forwarding
        is_forwarded = bool(forwarded_from) or (to_number == SIGNALWIRE_PHONE)
        
        # Log the call
        call_entry = {
            'time': datetime.now().strftime('%H:%M:%S'),
            'from': from_number,
            'type': 'Forwarded Call' if is_forwarded else 'Direct Call',
            'status': 'Incoming'
        }
        system_state.add_call_log(call_entry)
        
        # Update stats
        system_state.increment('calls_today')
        if is_forwarded:
            system_state.increment('calls_forwarded')
        
        response = VoiceResponse()
        
        # Greeting
        if is_forwarded:
            greeting = "Hi! Thanks for calling Jay's Mobile Wash. I'm Jay's AI assistant ready to help while Jay is with another customer. How can I assist you?"
        else:
            greeting = "Hi! Welcome to Jay's Mobile Wash! I'm your AI assistant ready to help with pricing, scheduling, or any questions. How can I help you?"
        
        response.say(greeting, voice='alice')
        
        # Gather speech
        response.gather(
            input='speech',
            timeout=10,
            action=f'/voice/process?call_sid={call_sid}&forwarded={is_forwarded}',
            speech_timeout=3
        )
        
        # Fallback
        response.say("I didn't catch that. Let me connect you with Jay.", voice='alice')
        response.dial(JAY_PHONE, timeout=30)
        
        return PlainTextResponse(str(response), media_type="application/xml")
        
    except Exception as e:
        logger.error(f"Voice error: {e}")
        response = VoiceResponse()
        response.say("Technical issue. Connecting you with Jay.", voice='alice')
        response.dial(JAY_PHONE, timeout=30)
        return PlainTextResponse(str(response), media_type="application/xml")

@fastapi_app.post("/voice/process")
async def process_speech(request: Request):
    """Process customer speech"""
    try:
        form = await request.form()
        query_params = request.query_params
        
        call_sid = query_params.get('call_sid')
        is_forwarded = query_params.get('forwarded') == 'True'
        speech_result = form.get('SpeechResult', '')
        confidence = float(form.get('Confidence', '0.0'))
        
        if not speech_result or confidence < 0.4:
            response = VoiceResponse()
            response.say("I didn't understand clearly. Let me connect you with Jay.", voice='alice')
            response.dial(JAY_PHONE, timeout=30)
            return PlainTextResponse(str(response), media_type="application/xml")
        
        # Analyze intent
        intent = ai.detect_intent(speech_result)
        
        # Check for escalation
        if intent in ['urgent', 'human_request'] or 'jay' in speech_result.lower():
            response = VoiceResponse()
            response.say("Let me connect you with Jay right away.", voice='alice')
            response.dial(JAY_PHONE, timeout=30)
            return PlainTextResponse(str(response), media_type="application/xml")
        
        # Generate AI response
        context = {'forwarded': is_forwarded, 'call_sid': call_sid}
        ai_response = ai.generate_response(speech_result, context)
        
        system_state.increment('ai_responses')
        
        response = VoiceResponse()
        response.say(ai_response, voice='alice')
        
        # Continue conversation
        response.gather(
            input='speech',
            timeout=8,
            action=f'/voice/process?call_sid={call_sid}&forwarded={is_forwarded}',
            speech_timeout=3
        )
        
        response.say("Thank you for calling Jay's Mobile Wash! Have a great day!", voice='alice')
        return PlainTextResponse(str(response), media_type="application/xml")
        
    except Exception as e:
        logger.error(f"Speech processing error: {e}")
        response = VoiceResponse()
        response.say("Technical issue. Connecting with Jay.", voice='alice')
        response.dial(JAY_PHONE, timeout=30)
        return PlainTextResponse(str(response), media_type="application/xml")

@fastapi_app.post("/sms/incoming")
async def handle_sms(request: Request):
    """Handle incoming SMS"""
    try:
        form = await request.form()
        
        message_sid = form.get('MessageSid')
        from_number = form.get('From')
        message_body = form.get('Body', '').strip()
        
        # Log the SMS
        sms_entry = {
            'time': datetime.now().strftime('%H:%M:%S'),
            'from': from_number,
            'message': message_body[:50] + '...' if len(message_body) > 50 else message_body,
            'status': 'Received'
        }
        system_state.add_sms_log(sms_entry)
        
        system_state.increment('sms_today')
        
        if not message_body:
            await send_sms("Hi! How can I help you with Jay's Mobile Wash today?", from_number)
            return PlainTextResponse(str(MessagingResponse()) if MessagingResponse else "", media_type="application/xml")
        
        # Analyze message
        intent = ai.detect_intent(message_body)
        sentiment = ai.analyze_sentiment(message_body)
        
        # Check for escalation
        if (intent in ['urgent', 'human_request'] or 
            sentiment['score'] < -0.6 or
            'jay' in message_body.lower()):
            
            response_text = f"I'll have Jay respond to you personally. For immediate assistance, call {JAY_PHONE}."
            await send_sms(response_text, from_number)
            return PlainTextResponse(str(MessagingResponse()) if MessagingResponse else "", media_type="application/xml")
        
        # Generate AI response
        ai_response = ai.generate_response(message_body, {'sms': True})
        
        # Optimize for SMS
        if len(ai_response) > 320:
            ai_response = ai_response[:317] + "..."
        
        system_state.increment('ai_responses')
        
        await send_sms(ai_response, from_number)
        return PlainTextResponse(str(MessagingResponse()) if MessagingResponse else "", media_type="application/xml")
        
    except Exception as e:
        logger.error(f"SMS error: {e}")
        await send_sms("Thanks for your message! Jay will get back to you soon.", from_number)
        return PlainTextResponse(str(MessagingResponse()) if MessagingResponse else "", media_type="application/xml")

async def send_sms(message, to_number):
    """Send SMS response"""
    try:
        if signalwire_client:
            signalwire_client.messages.create(
                body=message,
                from_=SIGNALWIRE_PHONE,
                to=to_number
            )
            
            # Log sent SMS
            sms_entry = {
                'time': datetime.now().strftime('%H:%M:%S'),
                'from': 'AI Assistant',
                'message': message[:50] + '...' if len(message) > 50 else message,
                'status': 'Sent'
            }
            system_state.add_sms_log(sms_entry)
        
    except Exception as e:
        logger.error(f"SMS send error: {e}")

@fastapi_app.get("/health")
async def health_check():
    """Health check endpoint"""
    health = {
        'status': 'healthy',
        'signalwire': bool(signalwire_client),
        'deepseek': bool(DEEPSEEK_API_KEY),
        'uptime': int((datetime.now() - system_state.get_all()['start_time']).total_seconds()),
        'timestamp': datetime.now().isoformat()
    }
    return health

# Gradio Interface Functions
def get_dashboard_data():
    """Get current dashboard data"""
    stats = system_state.get_all()
    uptime = datetime.now() - stats['start_time']
    
    dashboard_html = f"""
    <div style="font-family: Arial, sans-serif; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 15px; color: white;">
        <h1 style="text-align: center; margin-bottom: 30px;">πŸš— {BUSINESS_NAME} - AI Dashboard</h1>
        
        <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 30px;">
            <div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 10px; text-align: center;">
                <h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['calls_today']}</h2>
                <p style="margin: 5px 0 0 0;">πŸ“ž Calls Today</p>
            </div>
            <div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 10px; text-align: center;">
                <h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['sms_today']}</h2>
                <p style="margin: 5px 0 0 0;">πŸ“± SMS Today</p>
            </div>
            <div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 10px; text-align: center;">
                <h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['calls_forwarded']}</h2>
                <p style="margin: 5px 0 0 0;">πŸ”„ Calls Forwarded</p>
            </div>
            <div style="background: rgba(255,255,255,0.15); padding: 20px; border-radius: 10px; text-align: center;">
                <h2 style="color: #4facfe; font-size: 2em; margin: 0;">{stats['ai_responses']}</h2>
                <p style="margin: 5px 0 0 0;">πŸ€– AI Responses</p>
            </div>
        </div>
        
        <div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 10px; margin-bottom: 20px;">
            <h3 style="color: #4facfe; margin-bottom: 15px;">πŸ“ž iPhone Forwarding Status</h3>
            <p><strong>Jay's iPhone:</strong> {JAY_PHONE}</p>
            <p><strong>AI Assistant Number:</strong> {SIGNALWIRE_PHONE}</p>
            <p><strong>Forwarding:</strong> {'βœ… Enabled' if FORWARDING_ENABLED else '❌ Disabled'}</p>
            <p><strong>Delay:</strong> {FORWARDING_DELAY} seconds</p>
            <p><strong>SignalWire:</strong> {'βœ… Connected' if signalwire_client else '❌ Not configured'}</p>
            <p><strong>DeepSeek AI:</strong> {'βœ… Connected' if DEEPSEEK_API_KEY else '⚠️ Fallback mode'}</p>
        </div>
        
        <div style="background: rgba(255,255,255,0.1); padding: 20px; border-radius: 10px;">
            <h3 style="color: #4facfe; margin-bottom: 15px;">πŸ’Ό Services & Pricing</h3>
            <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px;">
    """
    
    for service_id, service in SERVICES.items():
        dashboard_html += f"""
                <div style="background: rgba(79, 172, 254, 0.2); padding: 15px; border-radius: 8px; text-align: center;">
                    <h4 style="margin: 0 0 5px 0; color: white;">{service['name']}</h4>
                    <p style="margin: 0 0 10px 0; font-size: 0.9em; opacity: 0.8;">{service['description']}</p>
                    <p style="margin: 0; font-size: 1.5em; font-weight: bold; color: #00ff88;">${service['price']:.0f}</p>
                </div>
        """
    
    dashboard_html += """
            </div>
        </div>
    </div>
    """
    
    return dashboard_html

def get_call_log():
    """Get recent call log"""
    stats = system_state.get_all()
    call_log = stats.get('call_log', [])
    
    if not call_log:
        return "No calls logged yet."
    
    log_html = """
    <div style="font-family: Arial, sans-serif;">
        <h3>πŸ“ž Recent Calls</h3>
        <div style="max-height: 300px; overflow-y: auto;">
    """
    
    for call in call_log[:10]:  # Show last 10 calls
        log_html += f"""
            <div style="background: #f5f5f5; padding: 10px; margin: 5px 0; border-radius: 5px; border-left: 4px solid #4facfe;">
                <strong>{call['time']}</strong> | {call['from']} | {call['type']} | {call['status']}
            </div>
        """
    
    log_html += "</div></div>"
    return log_html

def get_sms_log():
    """Get recent SMS log"""
    stats = system_state.get_all()
    sms_log = stats.get('sms_log', [])
    
    if not sms_log:
        return "No SMS logged yet."
    
    log_html = """
    <div style="font-family: Arial, sans-serif;">
        <h3>πŸ“± Recent SMS</h3>
        <div style="max-height: 300px; overflow-y: auto;">
    """
    
    for sms in sms_log[:10]:  # Show last 10 SMS
        log_html += f"""
            <div style="background: #f5f5f5; padding: 10px; margin: 5px 0; border-radius: 5px; border-left: 4px solid #00ff88;">
                <strong>{sms['time']}</strong> | {sms['from']}<br>
                <em>{sms['message']}</em> | {sms['status']}
            </div>
        """
    
    log_html += "</div></div>"
    return log_html

def test_ai_response(message):
    """Test AI response generation"""
    if not message.strip():
        return "Please enter a message to test."
    
    try:
        intent = ai.detect_intent(message)
        sentiment = ai.analyze_sentiment(message)
        response = ai.generate_response(message, {'test': True})
        
        result = f"""
        <div style="font-family: Arial, sans-serif; padding: 15px; background: #f9f9f9; border-radius: 10px;">
            <h3>AI Response Test</h3>
            <p><strong>Customer Message:</strong> {message}</p>
            <p><strong>Detected Intent:</strong> {intent}</p>
            <p><strong>Sentiment:</strong> {sentiment['label']} ({sentiment['score']:.2f})</p>
            <div style="background: #e3f2fd; padding: 10px; border-radius: 5px; margin-top: 10px;">
                <strong>AI Response:</strong><br>
                {response}
            </div>
        </div>
        """
        return result
    except Exception as e:
        return f"Error testing AI response: {e}"

# Start FastAPI in background thread
def run_fastapi():
    uvicorn.run(fastapi_app, host="0.0.0.0", port=8000, log_level="info")

fastapi_thread = threading.Thread(target=run_fastapi, daemon=True)
fastapi_thread.start()

# Create Gradio Interface
with gr.Blocks(
    title=f"{BUSINESS_NAME} - AI Dashboard",
    theme=gr.themes.Soft(),
    css="""
    .gradio-container {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    }
    """
) as interface:
    
    gr.Markdown(f"""
    # πŸš— {BUSINESS_NAME} - iPhone Forwarding AI System
    
    **Real-time dashboard for your AI assistant with iPhone call forwarding**
    
    πŸ“ž **Jay's iPhone:** {JAY_PHONE}  
    πŸ€– **AI Assistant:** {SIGNALWIRE_PHONE}  
    πŸ”„ **Forwarding:** {'βœ… Enabled' if FORWARDING_ENABLED else '❌ Disabled'}
    """)
    
    with gr.Tabs():
        with gr.Tab("πŸ“Š Dashboard"):
            dashboard_display = gr.HTML(value=get_dashboard_data())
            refresh_btn = gr.Button("πŸ”„ Refresh Dashboard", variant="primary")
            refresh_btn.click(fn=get_dashboard_data, outputs=dashboard_display)
        
        with gr.Tab("πŸ“ž Call Log"):
            call_log_display = gr.HTML(value=get_call_log())
            refresh_calls_btn = gr.Button("πŸ”„ Refresh Calls", variant="secondary")
            refresh_calls_btn.click(fn=get_call_log, outputs=call_log_display)
        
        with gr.Tab("πŸ“± SMS Log"):
            sms_log_display = gr.HTML(value=get_sms_log())
            refresh_sms_btn = gr.Button("πŸ”„ Refresh SMS", variant="secondary")
            refresh_sms_btn.click(fn=get_sms_log, outputs=sms_log_display)
        
        with gr.Tab("πŸ§ͺ Test AI"):
            with gr.Row():
                with gr.Column():
                    test_message = gr.Textbox(
                        label="Test Message",
                        placeholder="Enter a customer message to test AI response...",
                        lines=3
                    )
                    test_btn = gr.Button("πŸ€– Test AI Response", variant="primary")
                
                with gr.Column():
                    test_result = gr.HTML(label="AI Response")
            
            test_btn.click(fn=test_ai_response, inputs=test_message, outputs=test_result)
        
        with gr.Tab("ℹ️ Setup Info"):
            gr.Markdown(f"""
            ## πŸ”§ Webhook Configuration
            
            **Set these URLs in your SignalWire dashboard:**
            
            - **Voice Webhook:** `https://YOUR-SPACE-NAME.hf.space/voice/incoming`
            - **SMS Webhook:** `https://YOUR-SPACE-NAME.hf.space/sms/incoming`
            
            ## πŸ“± iPhone Setup
            
            **On Jay's iPhone:**
            1. Go to **Settings** β†’ **Phone** β†’ **Call Forwarding**
            2. Turn **ON** Call Forwarding  
            3. Set forward number to: `{SIGNALWIRE_PHONE}`
            4. Choose: Forward when **unanswered** after {FORWARDING_DELAY} seconds
            
            ## 🎯 How It Works
            
            1. Customer calls Jay's iPhone: `{JAY_PHONE}`
            2. iPhone rings for {FORWARDING_DELAY} seconds
            3. If no answer β†’ forwards to AI: `{SIGNALWIRE_PHONE}`
            4. AI handles call with forwarding awareness
            5. Can escalate back to Jay if needed
            
            ## πŸ“Š System Status
            
            - **SignalWire:** {'βœ… Connected' if signalwire_client else '❌ Not configured'}
            - **DeepSeek AI:** {'βœ… Connected' if DEEPSEEK_API_KEY else '⚠️ Using fallback responses'}
            - **FastAPI Webhooks:** βœ… Running on port 8000
            - **Health Check:** [/health](./health)
            """)
    
    # Auto-refresh every 30 seconds
    interface.load(fn=get_dashboard_data, outputs=dashboard_display, every=30)
    interface.load(fn=get_call_log, outputs=call_log_display, every=30)
    interface.load(fn=get_sms_log, outputs=sms_log_display, every=30)

# Launch the interface
if __name__ == "__main__":
    print("\n" + "="*60)
    print("πŸš— JAY'S MOBILE WASH AI SYSTEM")
    print("="*60)
    print(f"πŸ“ž Jay's Phone: {JAY_PHONE}")
    print(f"πŸ€– AI Phone: {SIGNALWIRE_PHONE}")
    print(f"πŸ”„ Forwarding: {'βœ… Enabled' if FORWARDING_ENABLED else '❌ Disabled'}")
    print(f"🌐 SignalWire: {'βœ… Connected' if signalwire_client else '❌ Not configured'}")
    print(f"🧠 DeepSeek: {'βœ… Connected' if DEEPSEEK_API_KEY else '❌ Not configured'}")
    print("="*60)
    print("πŸš€ Starting Gradio interface on port 7860...")
    print("πŸ“ž FastAPI webhooks running on port 8000")
    print("="*60)
    
    interface.launch(
        server_name="0.0.0.0",
        server_port=7860,
        share=False,
        show_error=True
    )