Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 892
0.00% covered (danger)
0.00%
0 / 30
CRAP
0.00% covered (danger)
0.00%
0 / 1
SellerMarketingController
0.00% covered (danger)
0.00%
0 / 892
0.00% covered (danger)
0.00%
0 / 30
16770
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getMessages
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
6
 createMessage
0.00% covered (danger)
0.00%
0 / 35
0.00% covered (danger)
0.00%
0 / 1
42
 updateMessage
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
90
 deleteMessage
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
20
 getTriggers
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
6
 createTrigger
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
56
 getTrigger
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
12
 updateTrigger
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
210
 toggleTrigger
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
20
 deleteTrigger
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
20
 getBlasts
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
90
 createBlast
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
42
 getBlastRecipients
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
12
 cancelBlast
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
20
 getBlastDetails
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
20
 getBlastLog
0.00% covered (danger)
0.00%
0 / 45
0.00% covered (danger)
0.00%
0 / 1
30
 exportBlastLog
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
42
 getQueueStats
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
 generateAIMessage
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 1
12
 moderateContent
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
12
 generateAICriteria
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
12
 buildTriggerFromPrompt
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
12
 getAIHelp
0.00% covered (danger)
0.00%
0 / 102
0.00% covered (danger)
0.00%
0 / 1
6
 sendTestMessage
0.00% covered (danger)
0.00%
0 / 55
0.00% covered (danger)
0.00%
0 / 1
56
 getMonitoringDashboard
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
6
 getQueueStatisticsData
0.00% covered (danger)
0.00%
0 / 23
0.00% covered (danger)
0.00%
0 / 1
12
 getTriggerStatisticsData
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
6
 getRecentActivityData
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getSystemHealthData
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2
3namespace BuyerKiosk\SellerMarketing\Controllers;
4
5use BuyerKiosk\Core\Controllers\BaseController;
6use BuyerKiosk\SellerMarketing\SellerMarketingMessage;
7use BuyerKiosk\SellerMarketing\SellerMarketingTrigger;
8use BuyerKiosk\SellerMarketing\SellerMarketingBlast;
9use BuyerKiosk\SellerMarketing\SellerMarketingQueue;
10use BuyerKiosk\SellerMarketing\OpenAIService;
11
12class SellerMarketingController extends BaseController {
13
14    private $store;
15    private $storeDB;
16    private $globalDB;
17
18    public function __construct($app, \Store $store)
19    {
20        parent::__construct($app);
21        $this->store = $store;
22        $this->storeDB = dbConnectByName($store->getDbName());
23        $this->globalDB = dbConnectByName('kiosk_buykiosk');
24    }
25
26    /**
27     * Get all messages for a store
28     */
29    public function getMessages()
30    {
31        try {
32            $messages = SellerMarketingMessage::getAllActive($this->storeDB);
33            
34            $response = array_map(function($message) {
35                return $message->toArray();
36            }, $messages);
37            
38            $this->_app->response->headers->set('Content-Type', 'application/json');
39            echo json_encode([
40                'success' => true,
41                'data' => $response
42            ]);
43        } catch (Exception $e) {
44            $this->_app->response->setStatus(500);
45            $this->_app->response->headers->set('Content-Type', 'application/json');
46            echo json_encode([
47                'success' => false,
48                'error' => $e->getMessage()
49            ]);
50        }
51    }
52
53    /**
54     * Create a new message
55     */
56    public function createMessage()
57    {
58        try {
59            $data = $this->_app->request->post();
60            
61            // Validate required fields
62            if (empty($data['name']) || empty($data['content'])) {
63                $this->_app->response->setStatus(400);
64                $this->_app->response->headers->set('Content-Type', 'application/json');
65                echo json_encode([
66                    'success' => false,
67                    'error' => 'Name and content are required'
68                ]);
69                return;
70            }
71            
72            $message = new SellerMarketingMessage(null, $this->storeDB);
73            $message->setName($data['name']);
74            $message->setContent($data['content']);
75            $message->setVariables($data['variables'] ?? []);
76            $message->setCategory($data['category'] ?? 'general');
77            $message->setActive(isset($data['active']) ? (bool)$data['active'] : true);
78            
79            if ($message->create()) {
80                $this->_app->response->setStatus(201);
81                $this->_app->response->headers->set('Content-Type', 'application/json');
82                echo json_encode([
83                    'success' => true,
84                    'data' => $message->toArray()
85                ]);
86            } else {
87                $this->_app->response->setStatus(500);
88                $this->_app->response->headers->set('Content-Type', 'application/json');
89                echo json_encode([
90                    'success' => false,
91                    'error' => 'Failed to create message'
92                ]);
93            }
94        } catch (Exception $e) {
95            $this->_app->response->setStatus(500);
96            $this->_app->response->headers->set('Content-Type', 'application/json');
97            echo json_encode([
98                'success' => false,
99                'error' => $e->getMessage()
100            ]);
101        }
102    }
103
104    /**
105     * Update an existing message
106     */
107    public function updateMessage($messageId)
108    {
109        try {
110            $message = new SellerMarketingMessage($messageId, $this->storeDB);
111            
112            if (!$message->getId()) {
113                $this->_app->response->setStatus(404);
114                $this->_app->response->headers->set('Content-Type', 'application/json');
115                echo json_encode([
116                    'success' => false,
117                    'error' => 'Message not found'
118                ]);
119                return;
120            }
121            
122            $data = $this->_app->request->post();
123            
124            if (isset($data['name'])) $message->setName($data['name']);
125            if (isset($data['content'])) $message->setContent($data['content']);
126            if (isset($data['variables'])) $message->setVariables($data['variables']);
127            if (isset($data['category'])) $message->setCategory($data['category']);
128            if (isset($data['active'])) $message->setActive((bool)$data['active']);
129            
130            if ($message->update()) {
131                $this->_app->response->headers->set('Content-Type', 'application/json');
132                echo json_encode([
133                    'success' => true,
134                    'data' => $message->toArray()
135                ]);
136            } else {
137                $this->_app->response->setStatus(500);
138                $this->_app->response->headers->set('Content-Type', 'application/json');
139                echo json_encode([
140                    'success' => false,
141                    'error' => 'Failed to update message'
142                ]);
143            }
144        } catch (Exception $e) {
145            $this->_app->response->setStatus(500);
146            $this->_app->response->headers->set('Content-Type', 'application/json');
147            echo json_encode([
148                'success' => false,
149                'error' => $e->getMessage()
150            ]);
151        }
152    }
153
154    /**
155     * Delete a message
156     */
157    public function deleteMessage($messageId)
158    {
159        try {
160            $message = new SellerMarketingMessage($messageId, $this->storeDB);
161            
162            if (!$message->getId()) {
163                $this->_app->response->setStatus(404);
164                $this->_app->response->headers->set('Content-Type', 'application/json');
165                echo json_encode([
166                    'success' => false,
167                    'error' => 'Message not found'
168                ]);
169                return;
170            }
171            
172            if ($message->delete()) {
173                $this->_app->response->headers->set('Content-Type', 'application/json');
174                echo json_encode([
175                    'success' => true,
176                    'message' => 'Message deleted successfully'
177                ]);
178            } else {
179                $this->_app->response->setStatus(500);
180                $this->_app->response->headers->set('Content-Type', 'application/json');
181                echo json_encode([
182                    'success' => false,
183                    'error' => 'Failed to delete message'
184                ]);
185            }
186        } catch (Exception $e) {
187            $this->_app->response->setStatus(500);
188            $this->_app->response->headers->set('Content-Type', 'application/json');
189            echo json_encode([
190                'success' => false,
191                'error' => $e->getMessage()
192            ]);
193        }
194    }
195
196    /**
197     * Get all triggers for a store
198     */
199    public function getTriggers()
200    {
201        try {
202            $triggers = SellerMarketingTrigger::getAllActive($this->storeDB);
203            
204            $response = array_map(function($trigger) {
205                return $trigger->toArray();
206            }, $triggers);
207            
208            $this->_app->response->headers->set('Content-Type', 'application/json');
209            echo json_encode([
210                'success' => true,
211                'data' => $response
212            ]);
213        } catch (Exception $e) {
214            $this->_app->response->setStatus(500);
215            $this->_app->response->headers->set('Content-Type', 'application/json');
216            echo json_encode([
217                'success' => false,
218                'error' => $e->getMessage()
219            ]);
220        }
221    }
222
223    /**
224     * Create a new trigger
225     */
226    public function createTrigger()
227    {
228        try {
229            $data = $this->_app->request->post();
230            
231            // Validate required fields
232            if (empty($data['name']) || empty($data['type']) || empty($data['message_id'])) {
233                $this->_app->response->setStatus(400);
234                $this->_app->response->headers->set('Content-Type', 'application/json');
235                echo json_encode([
236                    'success' => false,
237                    'error' => 'Name, type, and message_id are required'
238                ]);
239                return;
240            }
241            
242            $trigger = new SellerMarketingTrigger(null, $this->storeDB);
243            $trigger->setName($data['name']);
244            $trigger->setType($data['type']);
245            $trigger->setMessageId($data['message_id']);
246            $trigger->setStatus($data['status'] ?? SellerMarketingTrigger::STATUS_ACTIVE);
247            $trigger->setConfig($data['config'] ?? []);
248            $trigger->setExpireDate($data['expire_date'] ?? null);
249            $trigger->setProcessingFrequency($data['processing_frequency'] ?? SellerMarketingTrigger::FREQUENCY_DAILY);
250            $trigger->setTimeFrame($data['time_frame'] ?? null);
251            $trigger->setTimeRandom(isset($data['time_random']) ? (bool)$data['time_random'] : false);
252            $trigger->setActiveDays($data['active_days'] ?? ['monday','tuesday','wednesday','thursday','friday','saturday','sunday']);
253            
254            if ($trigger->create()) {
255                $this->_app->response->setStatus(201);
256                $this->_app->response->headers->set('Content-Type', 'application/json');
257                echo json_encode([
258                    'success' => true,
259                    'data' => $trigger->toArray()
260                ]);
261            } else {
262                $this->_app->response->setStatus(500);
263                $this->_app->response->headers->set('Content-Type', 'application/json');
264                echo json_encode([
265                    'success' => false,
266                    'error' => 'Failed to create trigger'
267                ]);
268            }
269        } catch (Exception $e) {
270            $this->_app->response->setStatus(500);
271            $this->_app->response->headers->set('Content-Type', 'application/json');
272            echo json_encode([
273                'success' => false,
274                'error' => $e->getMessage()
275            ]);
276        }
277    }
278
279    /**
280     * Get a single trigger by ID
281     */
282    public function getTrigger($triggerId)
283    {
284        try {
285            $trigger = new SellerMarketingTrigger($triggerId, $this->storeDB);
286
287            if (!$trigger->getId()) {
288                $this->_app->response->setStatus(404);
289                $this->_app->response->headers->set('Content-Type', 'application/json');
290                echo json_encode([
291                    'success' => false,
292                    'error' => 'Trigger not found'
293                ]);
294                return;
295            }
296
297            $this->_app->response->headers->set('Content-Type', 'application/json');
298            echo json_encode([
299                'success' => true,
300                'data' => $trigger->toArray()
301            ]);
302        } catch (Exception $e) {
303            $this->_app->response->setStatus(500);
304            $this->_app->response->headers->set('Content-Type', 'application/json');
305            echo json_encode([
306                'success' => false,
307                'error' => $e->getMessage()
308            ]);
309        }
310    }
311
312    /**
313     * Update an existing trigger
314     */
315    public function updateTrigger($triggerId)
316    {
317        try {
318            $trigger = new SellerMarketingTrigger($triggerId, $this->storeDB);
319
320            if (!$trigger->getId()) {
321                $this->_app->response->setStatus(404);
322                $this->_app->response->headers->set('Content-Type', 'application/json');
323                echo json_encode([
324                    'success' => false,
325                    'error' => 'Trigger not found'
326                ]);
327                return;
328            }
329            
330            $data = $this->_app->request->post();
331            
332            if (isset($data['name'])) $trigger->setName($data['name']);
333            if (isset($data['type'])) $trigger->setType($data['type']);
334            if (isset($data['message_id'])) $trigger->setMessageId($data['message_id']);
335            if (isset($data['status'])) $trigger->setStatus($data['status']);
336            if (isset($data['config'])) $trigger->setConfig($data['config']);
337            if (isset($data['expire_date'])) $trigger->setExpireDate($data['expire_date']);
338            if (isset($data['processing_frequency'])) $trigger->setProcessingFrequency($data['processing_frequency']);
339            if (isset($data['time_frame'])) $trigger->setTimeFrame($data['time_frame']);
340            if (isset($data['time_random'])) $trigger->setTimeRandom((bool)$data['time_random']);
341            if (isset($data['active_days'])) $trigger->setActiveDays($data['active_days']);
342            
343            if ($trigger->update()) {
344                $this->_app->response->headers->set('Content-Type', 'application/json');
345                echo json_encode([
346                    'success' => true,
347                    'data' => $trigger->toArray()
348                ]);
349            } else {
350                $this->_app->response->setStatus(500);
351                $this->_app->response->headers->set('Content-Type', 'application/json');
352                echo json_encode([
353                    'success' => false,
354                    'error' => 'Failed to update trigger'
355                ]);
356            }
357        } catch (Exception $e) {
358            $this->_app->response->setStatus(500);
359            $this->_app->response->headers->set('Content-Type', 'application/json');
360            echo json_encode([
361                'success' => false,
362                'error' => $e->getMessage()
363            ]);
364        }
365    }
366
367    /**
368     * Toggle trigger status
369     */
370    public function toggleTrigger($triggerId)
371    {
372        try {
373            $trigger = new SellerMarketingTrigger($triggerId, $this->storeDB);
374            
375            if (!$trigger->getId()) {
376                $this->_app->response->setStatus(404);
377                $this->_app->response->headers->set('Content-Type', 'application/json');
378                echo json_encode([
379                    'success' => false,
380                    'error' => 'Trigger not found'
381                ]);
382                return;
383            }
384            
385            if ($trigger->toggleStatus()) {
386                $this->_app->response->headers->set('Content-Type', 'application/json');
387                echo json_encode([
388                    'success' => true,
389                    'data' => $trigger->toArray()
390                ]);
391            } else {
392                $this->_app->response->setStatus(500);
393                $this->_app->response->headers->set('Content-Type', 'application/json');
394                echo json_encode([
395                    'success' => false,
396                    'error' => 'Failed to toggle trigger status'
397                ]);
398            }
399        } catch (Exception $e) {
400            $this->_app->response->setStatus(500);
401            $this->_app->response->headers->set('Content-Type', 'application/json');
402            echo json_encode([
403                'success' => false,
404                'error' => $e->getMessage()
405            ]);
406        }
407    }
408
409    /**
410     * Delete a trigger
411     */
412    public function deleteTrigger($triggerId)
413    {
414        try {
415            $trigger = new SellerMarketingTrigger($triggerId, $this->storeDB);
416            
417            if (!$trigger->getId()) {
418                $this->_app->response->setStatus(404);
419                $this->_app->response->headers->set('Content-Type', 'application/json');
420                echo json_encode([
421                    'success' => false,
422                    'error' => 'Trigger not found'
423                ]);
424                return;
425            }
426            
427            if ($trigger->delete()) {
428                $this->_app->response->headers->set('Content-Type', 'application/json');
429                echo json_encode([
430                    'success' => true,
431                    'message' => 'Trigger deleted successfully'
432                ]);
433            } else {
434                $this->_app->response->setStatus(500);
435                $this->_app->response->headers->set('Content-Type', 'application/json');
436                echo json_encode([
437                    'success' => false,
438                    'error' => 'Failed to delete trigger'
439                ]);
440            }
441        } catch (Exception $e) {
442            $this->_app->response->setStatus(500);
443            $this->_app->response->headers->set('Content-Type', 'application/json');
444            echo json_encode([
445                'success' => false,
446                'error' => $e->getMessage()
447            ]);
448        }
449    }
450
451    /**
452     * Get all blasts for a store
453     */
454    public function getBlasts()
455    {
456        try {
457            $blasts = SellerMarketingBlast::getRecent(50, $this->storeDB);
458            
459            $response = array_map(function($blast) {
460                $blastData = $blast->toArray();
461                
462                // Get the message content for each blast
463                if ($blast->getMessageId()) {
464                    try {
465                        $message = new SellerMarketingMessage($blast->getMessageId(), $this->storeDB);
466                        $messageContent = $message->getContent();
467                        
468                        // Replace variables with placeholder content for preview
469                        $placeholderContent = $messageContent;
470                        $placeholders = [
471                            '%customer%' => 'John Smith',
472                            '%company%' => $this->store->getCompanyName() ?: 'Your Store',
473                            '%store%' => $this->store->getCity() ?: 'Main Location',
474                            '%coop%' => ($this->store->getCompanyName() ?: 'Your Store') . ' Network',
475                            '%coupon_url%' => 'https://example.com/coupon',
476                            '%coupon_name%' => '$5 Off $30',
477                            '%phone%' => $this->store->getPhone() ?: '(555) 123-4567'
478                        ];
479                        
480                        foreach ($placeholders as $variable => $placeholder) {
481                            $placeholderContent = str_replace($variable, $placeholder, $placeholderContent);
482                        }
483                        
484                        $blastData['message_content'] = $placeholderContent;
485                        $blastData['message_content_raw'] = $messageContent; // Keep original for editing
486                    } catch (Exception $e) {
487                        error_log("Error loading message for blast " . $blast->getId() . ": " . $e->getMessage());
488                        $blastData['message_content'] = null;
489                        $blastData['message_content_raw'] = null;
490                    }
491                } else {
492                    $blastData['message_content'] = null;
493                    $blastData['message_content_raw'] = null;
494                }
495                
496                return $blastData;
497            }, $blasts);
498            
499            $this->_app->response->headers->set('Content-Type', 'application/json');
500            echo json_encode([
501                'success' => true,
502                'data' => $response
503            ]);
504        } catch (Exception $e) {
505            $this->_app->response->setStatus(500);
506            $this->_app->response->headers->set('Content-Type', 'application/json');
507            echo json_encode([
508                'success' => false,
509                'error' => $e->getMessage()
510            ]);
511        }
512    }
513
514    /**
515     * Create a new blast
516     */
517    public function createBlast()
518    {
519        try {
520            $data = $this->_app->request->post();
521            
522            // Validate required fields
523            if (empty($data['name']) || empty($data['message_id']) || empty($data['scheduled_at'])) {
524                $this->_app->response->setStatus(400);
525                $this->_app->response->headers->set('Content-Type', 'application/json');
526                echo json_encode([
527                    'success' => false,
528                    'error' => 'Name, message_id, and scheduled_at are required'
529                ]);
530                return;
531            }
532            
533            $blast = new SellerMarketingBlast(null, $this->storeDB);
534            $blast->setName($data['name']);
535            $blast->setMessageId($data['message_id']);
536            $blast->setCriteria($data['criteria'] ?? []);
537            $blast->setScheduledAt($data['scheduled_at']);
538            $blast->setStatus(SellerMarketingBlast::STATUS_PENDING);
539            $blast->setCreatedBy($this->_app->user->user_id ?? null);
540            
541            if ($blast->create()) {
542                $this->_app->response->setStatus(201);
543                $this->_app->response->headers->set('Content-Type', 'application/json');
544                echo json_encode([
545                    'success' => true,
546                    'data' => $blast->toArray()
547                ]);
548            } else {
549                $this->_app->response->setStatus(500);
550                $this->_app->response->headers->set('Content-Type', 'application/json');
551                echo json_encode([
552                    'success' => false,
553                    'error' => 'Failed to create blast'
554                ]);
555            }
556        } catch (Exception $e) {
557            $this->_app->response->setStatus(500);
558            $this->_app->response->headers->set('Content-Type', 'application/json');
559            echo json_encode([
560                'success' => false,
561                'error' => $e->getMessage()
562            ]);
563        }
564    }
565
566    /**
567     * Get blast recipients count
568     */
569    public function getBlastRecipients()
570    {
571        try {
572            $data = $this->_app->request->post();
573            
574            if (empty($data['criteria'])) {
575                $this->_app->response->setStatus(400);
576                $this->_app->response->headers->set('Content-Type', 'application/json');
577                echo json_encode([
578                    'success' => false,
579                    'error' => 'Criteria is required'
580                ]);
581                return;
582            }
583            
584            // Create temporary blast to get recipients
585            $tempBlast = new SellerMarketingBlast(null, $this->storeDB);
586            $tempBlast->setCriteria($data['criteria']);
587            
588            $storeInfo = getStoreInfo($this->store);
589            $recipients = $tempBlast->getRecipients($storeInfo);
590            
591            $this->_app->response->headers->set('Content-Type', 'application/json');
592            echo json_encode([
593                'success' => true,
594                'data' => [
595                    'count' => count($recipients),
596                    'recipients' => array_slice($recipients, 0, 10) // Return first 10 for preview
597                ]
598            ]);
599        } catch (Exception $e) {
600            $this->_app->response->setStatus(500);
601            $this->_app->response->headers->set('Content-Type', 'application/json');
602            echo json_encode([
603                'success' => false,
604                'error' => $e->getMessage()
605            ]);
606        }
607    }
608
609    /**
610     * Cancel a blast
611     */
612    public function cancelBlast($blastId)
613    {
614        try {
615            $blast = new SellerMarketingBlast($blastId, $this->storeDB);
616            
617            if (!$blast->getId()) {
618                $this->_app->response->setStatus(404);
619                $this->_app->response->headers->set('Content-Type', 'application/json');
620                echo json_encode([
621                    'success' => false,
622                    'error' => 'Blast not found'
623                ]);
624                return;
625            }
626            
627            if ($blast->cancel()) {
628                $this->_app->response->headers->set('Content-Type', 'application/json');
629                echo json_encode([
630                    'success' => true,
631                    'data' => $blast->toArray()
632                ]);
633            } else {
634                $this->_app->response->setStatus(500);
635                $this->_app->response->headers->set('Content-Type', 'application/json');
636                echo json_encode([
637                    'success' => false,
638                    'error' => 'Failed to cancel blast'
639                ]);
640            }
641        } catch (Exception $e) {
642            $this->_app->response->setStatus(500);
643            $this->_app->response->headers->set('Content-Type', 'application/json');
644            echo json_encode([
645                'success' => false,
646                'error' => $e->getMessage()
647            ]);
648        }
649    }
650
651    /**
652     * Get blast details for detail page
653     */
654    public function getBlastDetails($blastId)
655    {
656        try {
657            $blast = new SellerMarketingBlast($blastId, $this->storeDB);
658            
659            if (!$blast->getId()) {
660                $this->_app->notFound();
661                return;
662            }
663            
664            // Get the blast data and message content
665            $blastData = $blast->toArray();
666            
667            if ($blast->getMessageId()) {
668                $message = new SellerMarketingMessage($blast->getMessageId(), $this->storeDB);
669                $blastData['message_content'] = $message->getContent();
670            } else {
671                $blastData['message_content'] = null;
672            }
673            
674            $this->_app->render('sellermarketing/blast-detail.html', [
675                'blast' => $blastData,
676                'store' => $this->store
677            ]);
678        } catch (Exception $e) {
679            error_log("Error in getBlastDetails: " . $e->getMessage());
680            $this->_app->halt(500, 'Internal Server Error');
681        }
682    }
683
684    /**
685     * Get blast log data for DataTable
686     */
687    public function getBlastLog($blastId)
688    {
689        try {
690            $blast = new SellerMarketingBlast($blastId, $this->storeDB);
691            
692            if (!$blast->getId()) {
693                $this->_app->response->setStatus(404);
694                $this->_app->response->headers->set('Content-Type', 'application/json');
695                echo json_encode([
696                    'success' => false,
697                    'error' => 'Blast not found'
698                ]);
699                return;
700            }
701            
702            // Get log entries from seller_marketing_customer_log table
703            // Note: seller_marketing_queue is in global DB, so we'll get provider info separately if needed
704            $query = "SELECT 
705                        sml.id,
706                        sml.phone,
707                        sml.sent_at,
708                        sml.status,
709                        sml.provider_id,
710                        'unknown' as delivery_status,
711                        '' as error_message,
712                        c.firstName,
713                        c.lastName,
714                        'unknown' as provider
715                      FROM seller_marketing_customer_log sml
716                      LEFT JOIN customers c ON sml.customer_id = c.customerID
717                      WHERE sml.blast_id = :blast_id
718                      ORDER BY sml.sent_at DESC";
719            
720            $stmt = $this->storeDB->prepare($query);
721            $stmt->bindParam(':blast_id', $blastId, \PDO::PARAM_INT);
722            
723            if ($stmt->execute()) {
724                $logs = $stmt->fetchAll(\PDO::FETCH_ASSOC);
725                
726                // Format the data
727                $formattedLogs = array_map(function($log) {
728                    return [
729                        'id' => $log['id'],
730                        'customer_name' => trim(($log['firstName'] ?? '') . ' ' . ($log['lastName'] ?? '')),
731                        'phone' => $log['phone'],
732                        'status' => $log['status'],
733                        'delivery_status' => $log['delivery_status'],
734                        'sent_at' => $log['sent_at'],
735                        'provider' => $log['provider'] ?: 'Unknown',
736                        'error_message' => $log['error_message']
737                    ];
738                }, $logs);
739                
740                $this->_app->response->headers->set('Content-Type', 'application/json');
741                echo json_encode([
742                    'success' => true,
743                    'data' => $formattedLogs
744                ]);
745            } else {
746                $this->_app->response->setStatus(500);
747                $this->_app->response->headers->set('Content-Type', 'application/json');
748                echo json_encode([
749                    'success' => false,
750                    'error' => 'Failed to fetch log data'
751                ]);
752            }
753        } catch (Exception $e) {
754            $this->_app->response->setStatus(500);
755            $this->_app->response->headers->set('Content-Type', 'application/json');
756            echo json_encode([
757                'success' => false,
758                'error' => $e->getMessage()
759            ]);
760        }
761    }
762
763    /**
764     * Export blast log as CSV
765     */
766    public function exportBlastLog($blastId)
767    {
768        try {
769            $blast = new SellerMarketingBlast($blastId, $this->storeDB);
770            
771            if (!$blast->getId()) {
772                $this->_app->notFound();
773                return;
774            }
775            
776            // Get log entries
777            $query = "SELECT 
778                        sml.phone,
779                        sml.sent_at,
780                        sml.status,
781                        'unknown' as delivery_status,
782                        '' as error_message,
783                        c.firstName,
784                        c.lastName,
785                        'unknown' as provider
786                      FROM seller_marketing_customer_log sml
787                      LEFT JOIN customers c ON sml.customer_id = c.customerID
788                      WHERE sml.blast_id = :blast_id
789                      ORDER BY sml.sent_at DESC";
790            
791            $stmt = $this->storeDB->prepare($query);
792            $stmt->bindParam(':blast_id', $blastId, \PDO::PARAM_INT);
793            
794            if ($stmt->execute()) {
795                $logs = $stmt->fetchAll(\PDO::FETCH_ASSOC);
796                
797                // Generate CSV
798                $filename = 'blast_' . $blastId . '_log_' . date('Y-m-d_H-i-s') . '.csv';
799                
800                $this->_app->response->headers->set('Content-Type', 'text/csv');
801                $this->_app->response->headers->set('Content-Disposition', 'attachment; filename="' . $filename . '"');
802                
803                $output = fopen('php://output', 'w');
804                
805                // CSV headers
806                fputcsv($output, [
807                    'Customer Name',
808                    'Phone',
809                    'Status',
810                    'Delivery Status',
811                    'Sent At',
812                    'Provider',
813                    'Error Message'
814                ]);
815                
816                // CSV data
817                foreach ($logs as $log) {
818                    fputcsv($output, [
819                        trim(($log['firstName'] ?? '') . ' ' . ($log['lastName'] ?? '')),
820                        $log['phone'],
821                        $log['status'],
822                        $log['delivery_status'],
823                        $log['sent_at'],
824                        $log['provider'] ?: 'Unknown',
825                        $log['error_message']
826                    ]);
827                }
828                
829                fclose($output);
830            } else {
831                $this->_app->halt(500, 'Failed to fetch log data');
832            }
833        } catch (Exception $e) {
834            error_log("Error in exportBlastLog: " . $e->getMessage());
835            $this->_app->halt(500, 'Internal Server Error');
836        }
837    }
838
839    /**
840     * Get queue statistics
841     */
842    public function getQueueStats()
843    {
844        try {
845            $stats = SellerMarketingQueue::getStoreStats($this->store->getId(), $this->globalDB);
846            
847            $this->_app->response->headers->set('Content-Type', 'application/json');
848            echo json_encode([
849                'success' => true,
850                'data' => $stats
851            ]);
852        } catch (Exception $e) {
853            $this->_app->response->setStatus(500);
854            $this->_app->response->headers->set('Content-Type', 'application/json');
855            echo json_encode([
856                'success' => false,
857                'error' => $e->getMessage()
858            ]);
859        }
860    }
861
862    /**
863     * Generate AI-powered marketing message
864     */
865    public function generateAIMessage()
866    {
867        try {
868            // Log request details
869            error_log("OpenAI generateMessage called with data: " . json_encode($_POST));
870            error_log("OpenAI generateMessage store: " . $this->store->getTypeNum());
871            
872            $data = $this->_app->request->post();
873            
874            if (empty($data['user_input'])) {
875                error_log("OpenAI generateMessage: Missing user_input");
876                $this->_app->response->setStatus(400);
877                $this->_app->response->headers->set('Content-Type', 'application/json');
878                echo json_encode([
879                    'success' => false,
880                    'error' => 'User input is required'
881                ]);
882                return;
883            }
884            
885            error_log("OpenAI generateMessage: Creating OpenAI service");
886            $openAI = new OpenAIService();
887            
888            // Get store information for context
889            $storeInfo = [
890                'store_type' => getStoreType($this->store),
891                'city' => $this->store->getCity(),
892                'tone' => $data['tone'] ?? 'friendly',
893                'target_audience' => $data['target_audience'] ?? 'customers'
894            ];
895            
896            error_log("OpenAI generateMessage: Store info: " . json_encode($storeInfo));
897            error_log("OpenAI generateMessage: Calling OpenAI API");
898            
899            $result = $openAI->generateMessage($data['user_input'], $storeInfo);
900            
901            error_log("OpenAI generateMessage: Result: " . json_encode($result));
902            
903            $this->_app->response->headers->set('Content-Type', 'application/json');
904            echo json_encode($result);
905            
906        } catch (Exception $e) {
907            // Log detailed error information
908            error_log("OpenAI generateMessage EXCEPTION: " . $e->getMessage());
909            error_log("OpenAI generateMessage TRACE: " . $e->getTraceAsString());
910            error_log("OpenAI generateMessage REQUEST: " . json_encode($_POST));
911            
912            $this->_app->response->setStatus(500);
913            $this->_app->response->headers->set('Content-Type', 'application/json');
914            echo json_encode([
915                'success' => false,
916                'error' => $e->getMessage(),
917                'debug' => [
918                    'file' => $e->getFile(),
919                    'line' => $e->getLine(),
920                    'class' => get_class($e)
921                ]
922            ]);
923        }
924    }
925
926    /**
927     * Moderate message content for inappropriate material
928     */
929    public function moderateContent()
930    {
931        try {
932            $data = $this->_app->request->post();
933            
934            if (empty($data['content'])) {
935                $this->_app->response->setStatus(400);
936                $this->_app->response->headers->set('Content-Type', 'application/json');
937                echo json_encode([
938                    'success' => false,
939                    'error' => 'Content is required'
940                ]);
941                return;
942            }
943            
944            $openAI = new OpenAIService();
945            $result = $openAI->moderateContent($data['content']);
946            
947            $this->_app->response->headers->set('Content-Type', 'application/json');
948            echo json_encode($result);
949            
950        } catch (Exception $e) {
951            $this->_app->response->setStatus(500);
952            $this->_app->response->headers->set('Content-Type', 'application/json');
953            echo json_encode([
954                'success' => false,
955                'error' => $e->getMessage()
956            ]);
957        }
958    }
959
960    /**
961     * Generate AI-powered customer targeting criteria
962     */
963    public function generateAICriteria()
964    {
965        try {
966            $data = $this->_app->request->post();
967            
968            if (empty($data['user_input'])) {
969                $this->_app->response->setStatus(400);
970                $this->_app->response->headers->set('Content-Type', 'application/json');
971                echo json_encode([
972                    'success' => false,
973                    'error' => 'User input is required'
974                ]);
975                return;
976            }
977            
978            $openAI = new OpenAIService();
979            $storeData = $data['store_data'] ?? [];
980            
981            $result = $openAI->generateCriteria($data['user_input'], $storeData);
982            
983            $this->_app->response->headers->set('Content-Type', 'application/json');
984            echo json_encode($result);
985            
986        } catch (Exception $e) {
987            $this->_app->response->setStatus(500);
988            $this->_app->response->headers->set('Content-Type', 'application/json');
989            echo json_encode([
990                'success' => false,
991                'error' => $e->getMessage()
992            ]);
993        }
994    }
995
996    /**
997     * Build a complete trigger configuration from natural language
998     * This is the key Phase 3 feature for AI-powered trigger building
999     */
1000    public function buildTriggerFromPrompt()
1001    {
1002        try {
1003            $data = $this->_app->request->post();
1004
1005            if (empty($data['prompt'])) {
1006                $this->_app->response->setStatus(400);
1007                $this->_app->response->headers->set('Content-Type', 'application/json');
1008                echo json_encode([
1009                    'success' => false,
1010                    'error' => 'Prompt is required'
1011                ]);
1012                return;
1013            }
1014
1015            $openAI = new OpenAIService();
1016
1017            // Get store context
1018            $storeContext = [
1019                'name' => $this->store->getStoreName(),
1020                'type' => getStoreType($this->store),
1021                'city' => $this->store->getCity()
1022            ];
1023
1024            $result = $openAI->buildTriggerFromPrompt($data['prompt'], $storeContext);
1025
1026            $this->_app->response->headers->set('Content-Type', 'application/json');
1027            echo json_encode($result);
1028
1029        } catch (Exception $e) {
1030            $this->_app->response->setStatus(500);
1031            $this->_app->response->headers->set('Content-Type', 'application/json');
1032            echo json_encode([
1033                'success' => false,
1034                'error' => $e->getMessage()
1035            ]);
1036        }
1037    }
1038
1039    /**
1040     * Get AI help examples and documentation
1041     */
1042    public function getAIHelp()
1043    {
1044        try {
1045            $helpData = [
1046                'message_generation' => [
1047                    'title' => 'AI Message Generation',
1048                    'description' => 'Generate professional marketing messages using AI',
1049                    'examples' => [
1050                        [
1051                            'input' => 'Create a welcome message for new customers',
1052                            'context' => ['business_type' => 'retail', 'tone' => 'friendly'],
1053                            'output' => 'Welcome to %company%! We\'re excited to have you. Use code WELCOME10 for 10% off your first purchase!'
1054                        ],
1055                        [
1056                            'input' => 'Remind customers about abandoned carts',
1057                            'context' => ['business_type' => 'online store', 'tone' => 'urgent'],
1058                            'output' => 'Hi %customer%! You left something in your cart. Complete your purchase now and get free shipping!'
1059                        ],
1060                        [
1061                            'input' => 'Promote a seasonal sale',
1062                            'context' => ['business_type' => 'fashion', 'tone' => 'exciting'],
1063                            'output' => 'FLASH SALE! 50% off all winter items at %company%! Today only - shop now!'
1064                        ]
1065                    ],
1066                    'tips' => [
1067                        'Be specific about your campaign goal',
1068                        'Provide business context for better results',
1069                        'Specify the desired tone (friendly, urgent, professional, etc.)',
1070                        'Include any special offers or promotions',
1071                        'Consider your target audience'
1072                    ]
1073                ],
1074                'content_moderation' => [
1075                    'title' => 'AI Content Moderation',
1076                    'description' => 'Automatically detect and filter inappropriate content',
1077                    'blocked_content' => [
1078                        'Sexual content',
1079                        'Hate speech',
1080                        'Harassment',
1081                        'Violence',
1082                        'Alcohol and tobacco',
1083                        'Drugs and substances',
1084                        'Self-harm'
1085                    ],
1086                    'examples' => [
1087                        [
1088                            'content' => 'Great deals on wine this weekend!',
1089                            'flagged' => true,
1090                            'reason' => 'Alcohol-related content'
1091                        ],
1092                        [
1093                            'content' => 'Flash sale on electronics!',
1094                            'flagged' => false,
1095                            'reason' => 'Clean promotional content'
1096                        ]
1097                    ],
1098                    'tips' => [
1099                        'Check all messages before sending',
1100                        'Review flagged content carefully',
1101                        'Consider industry-specific regulations',
1102                        'Use alternative language for blocked content'
1103                    ]
1104                ],
1105                'criteria_generation' => [
1106                    'title' => 'AI Criteria Building',
1107                    'description' => 'Generate smart customer targeting criteria',
1108                    'examples' => [
1109                        [
1110                            'input' => 'Target customers who haven\'t visited in a while',
1111                            'output' => [
1112                                ['field' => 'days_since_last_visit', 'operator' => '>', 'value' => 30],
1113                                ['field' => 'customer_rating', 'operator' => '>=', 'value' => 3],
1114                                ['field' => 'lifetime_value', 'operator' => '>', 'value' => 100]
1115                            ]
1116                        ],
1117                        [
1118                            'input' => 'Find high-value customers for VIP promotion',
1119                            'output' => [
1120                                ['field' => 'lifetime_value', 'operator' => '>', 'value' => 1000],
1121                                ['field' => 'purchase_frequency', 'operator' => '=', 'value' => 'frequent'],
1122                                ['field' => 'customer_rating', 'operator' => '>=', 'value' => 4]
1123                            ]
1124                        ]
1125                    ],
1126                    'tips' => [
1127                        'Describe your campaign goal clearly',
1128                        'Specify the type of customers you want to target',
1129                        'Consider customer behavior patterns',
1130                        'Review and adjust AI suggestions',
1131                        'Test with small groups first'
1132                    ]
1133                ]
1134            ];
1135            
1136            $this->_app->response->headers->set('Content-Type', 'application/json');
1137            echo json_encode([
1138                'success' => true,
1139                'data' => $helpData
1140            ]);
1141            
1142        } catch (Exception $e) {
1143            $this->_app->response->setStatus(500);
1144            $this->_app->response->headers->set('Content-Type', 'application/json');
1145            echo json_encode([
1146                'success' => false,
1147                'error' => $e->getMessage()
1148            ]);
1149        }
1150    }
1151
1152    /**
1153     * Send a test message to verify SMS functionality
1154     */
1155    public function sendTestMessage()
1156    {
1157        try {
1158            $data = $this->_app->request->post();
1159            
1160            // Validate required fields
1161            if (empty($data['phone']) || empty($data['message'])) {
1162                $this->_app->response->setStatus(400);
1163                $this->_app->response->headers->set('Content-Type', 'application/json');
1164                echo json_encode([
1165                    'success' => false,
1166                    'error' => 'Phone number and message are required'
1167                ]);
1168                return;
1169            }
1170            
1171            // Clean phone number (remove non-digits)
1172            $phone = preg_replace('/[^0-9]/', '', $data['phone']);
1173            
1174            // Validate phone number length
1175            if (strlen($phone) !== 10) {
1176                $this->_app->response->setStatus(400);
1177                $this->_app->response->headers->set('Content-Type', 'application/json');
1178                echo json_encode([
1179                    'success' => false,
1180                    'error' => 'Phone number must be 10 digits'
1181                ]);
1182                return;
1183            }
1184            
1185            // Create test message queue entry
1186            $queueItem = new SellerMarketingQueue(null, $this->globalDB);
1187            $queueItem->setStoreId($this->store->getStoreID());
1188            $queueItem->setPhone($phone);
1189            $queueItem->setMessage($data['message']);
1190            $queueItem->setMessageType('test');
1191            $queueItem->setSendAt(date('Y-m-d H:i:s'));
1192            $queueItem->setPriority(1); // High priority for test messages
1193            
1194            if ($queueItem->queue()) {
1195                // Try to send immediately
1196                $result = $queueItem->send();
1197                
1198                $this->_app->response->headers->set('Content-Type', 'application/json');
1199                
1200                if ($result) {
1201                    echo json_encode([
1202                        'success' => true,
1203                        'message' => 'Test message queued and sent successfully',
1204                        'queue_id' => $queueItem->getId(),
1205                        'provider' => $queueItem->getProvider(),
1206                        'status' => $queueItem->getStatus()
1207                    ]);
1208                } else {
1209                    echo json_encode([
1210                        'success' => false,
1211                        'message' => 'Test message queued but failed to send',
1212                        'queue_id' => $queueItem->getId(),
1213                        'error' => $queueItem->getErrorMessage()
1214                    ]);
1215                }
1216            } else {
1217                $this->_app->response->setStatus(500);
1218                $this->_app->response->headers->set('Content-Type', 'application/json');
1219                echo json_encode([
1220                    'success' => false,
1221                    'error' => 'Failed to queue test message'
1222                ]);
1223            }
1224            
1225        } catch (\Exception $e) {
1226            $this->_app->response->setStatus(500);
1227            $this->_app->response->headers->set('Content-Type', 'application/json');
1228            echo json_encode([
1229                'success' => false,
1230                'error' => $e->getMessage()
1231            ]);
1232        }
1233    }
1234
1235    /**
1236     * Get monitoring dashboard data
1237     * Phase 3: Queue System monitoring endpoint
1238     */
1239    public function getMonitoringDashboard()
1240    {
1241        try {
1242            // Queue statistics
1243            $queueStats = $this->getQueueStatisticsData();
1244
1245            // Trigger statistics
1246            $triggerStats = $this->getTriggerStatisticsData();
1247
1248            // Recent activity
1249            $recentActivity = $this->getRecentActivityData();
1250
1251            // System health
1252            $systemHealth = $this->getSystemHealthData();
1253
1254            $dashboard = [
1255                'queue' => $queueStats,
1256                'triggers' => $triggerStats,
1257                'recent_activity' => $recentActivity,
1258                'system_health' => $systemHealth,
1259                'timestamp' => date('Y-m-d H:i:s')
1260            ];
1261
1262            $this->_app->response->headers->set('Content-Type', 'application/json');
1263            echo json_encode([
1264                'success' => true,
1265                'data' => $dashboard
1266            ]);
1267
1268        } catch (\Exception $e) {
1269            $this->_app->response->setStatus(500);
1270            $this->_app->response->headers->set('Content-Type', 'application/json');
1271            echo json_encode([
1272                'success' => false,
1273                'error' => $e->getMessage()
1274            ]);
1275        }
1276    }
1277
1278    /**
1279     * Get queue statistics data
1280     */
1281    private function getQueueStatisticsData()
1282    {
1283        // Get counts by status
1284        $stmt = $this->globalDB->query("
1285            SELECT
1286                status,
1287                COUNT(*) as count
1288            FROM seller_marketing_queue
1289            WHERE store_id = '{$this->store->getTypeNum()}'
1290            GROUP BY status
1291        ");
1292
1293        $statusCounts = [];
1294        foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
1295            $statusCounts[$row['status']] = (int)$row['count'];
1296        }
1297
1298        // Get recent stats (last 24 hours)
1299        $stmt = $this->globalDB->query("
1300            SELECT
1301                COUNT(*) as total,
1302                SUM(CASE WHEN status = 'sent' THEN 1 ELSE 0 END) as sent,
1303                SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed
1304            FROM seller_marketing_queue
1305            WHERE store_id = '{$this->store->getTypeNum()}'
1306            AND created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
1307        ");
1308
1309        $recentStats = $stmt->fetch(\PDO::FETCH_ASSOC);
1310
1311        return [
1312            'by_status' => $statusCounts,
1313            'last_24h' => [
1314                'total' => (int)$recentStats['total'],
1315                'sent' => (int)$recentStats['sent'],
1316                'failed' => (int)$recentStats['failed'],
1317                'success_rate' => $recentStats['total'] > 0
1318                    ? round(($recentStats['sent'] / $recentStats['total']) * 100, 2)
1319                    : 0
1320            ],
1321            'pending' => $statusCounts['pending'] ?? 0,
1322            'processing' => $statusCounts['processing'] ?? 0
1323        ];
1324    }
1325
1326    /**
1327     * Get trigger statistics data
1328     */
1329    private function getTriggerStatisticsData()
1330    {
1331        // Get trigger counts by status
1332        $stmt = $this->storeDB->query("
1333            SELECT
1334                status,
1335                COUNT(*) as count
1336            FROM seller_marketing_triggers
1337            GROUP BY status
1338        ");
1339
1340        $statusCounts = [];
1341        foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
1342            $statusCounts[$row['status']] = (int)$row['count'];
1343        }
1344
1345        // Get recently processed triggers
1346        $stmt = $this->storeDB->query("
1347            SELECT
1348                COUNT(*) as count
1349            FROM seller_marketing_triggers
1350            WHERE last_processed >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
1351        ");
1352
1353        $recentlyProcessed = (int)$stmt->fetchColumn();
1354
1355        return [
1356            'by_status' => $statusCounts,
1357            'active' => $statusCounts['active'] ?? 0,
1358            'inactive' => $statusCounts['inactive'] ?? 0,
1359            'expired' => $statusCounts['expired'] ?? 0,
1360            'processed_last_24h' => $recentlyProcessed
1361        ];
1362    }
1363
1364    /**
1365     * Get recent activity data
1366     */
1367    private function getRecentActivityData()
1368    {
1369        // Get recent messages
1370        $stmt = $this->globalDB->prepare("
1371            SELECT
1372                id,
1373                message_type,
1374                status,
1375                created_at,
1376                send_at
1377            FROM seller_marketing_queue
1378            WHERE store_id = :store_id
1379            ORDER BY created_at DESC
1380            LIMIT 10
1381        ");
1382
1383        $stmt->execute([':store_id' => $this->store->getTypeNum()]);
1384
1385        return [
1386            'recent_messages' => $stmt->fetchAll(\PDO::FETCH_ASSOC)
1387        ];
1388    }
1389
1390    /**
1391     * Get system health data
1392     */
1393    private function getSystemHealthData()
1394    {
1395        // Check for stuck messages (processing for > 1 hour)
1396        $stmt = $this->globalDB->prepare("
1397            SELECT COUNT(*) as count
1398            FROM seller_marketing_queue
1399            WHERE store_id = :store_id
1400            AND status = 'processing'
1401            AND created_at < DATE_SUB(NOW(), INTERVAL 1 HOUR)
1402        ");
1403
1404        $stmt->execute([':store_id' => $this->store->getTypeNum()]);
1405        $stuckMessages = (int)$stmt->fetchColumn();
1406
1407        // Check for high failure rate
1408        $stmt = $this->globalDB->prepare("
1409            SELECT
1410                SUM(CASE WHEN status = 'sent' THEN 1 ELSE 0 END) as sent,
1411                SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed
1412            FROM seller_marketing_queue
1413            WHERE store_id = :store_id
1414            AND created_at >= DATE_SUB(NOW(), INTERVAL 1 HOUR)
1415        ");
1416
1417        $stmt->execute([':store_id' => $this->store->getTypeNum()]);
1418        $hourlyStats = $stmt->fetch(\PDO::FETCH_ASSOC);
1419
1420        $failureRate = 0;
1421        $total = $hourlyStats['sent'] + $hourlyStats['failed'];
1422        if ($total > 0) {
1423            $failureRate = round(($hourlyStats['failed'] / $total) * 100, 2);
1424        }
1425
1426        // Determine health status
1427        $health = 'good';
1428        $issues = [];
1429
1430        if ($stuckMessages > 0) {
1431            $health = 'warning';
1432            $issues[] = "{$stuckMessages} messages stuck in processing";
1433        }
1434
1435        if ($failureRate > 10) {
1436            $health = 'warning';
1437            $issues[] = "High failure rate: {$failureRate}%";
1438        }
1439
1440        if ($failureRate > 25) {
1441            $health = 'critical';
1442        }
1443
1444        return [
1445            'status' => $health,
1446            'stuck_messages' => $stuckMessages,
1447            'hourly_failure_rate' => $failureRate,
1448            'issues' => $issues
1449        ];
1450    }
1451}