07-notifications-multi-canaux

<?php
interface Notifier {
    public function send(string $recipient, string $subject, string $message): bool;
    public function sendBulk(array $recipients, string $subject, string $message): array;
    public function schedule(string $recipient, string $subject, string $message, DateTime $when): bool;
    public function getDeliveryStatus(string $messageId): string;
    public function cancelScheduled(string $messageId): bool;
}

class EmailNotifier implements Notifier {
    private SMTPConnection $smtp;
    
    public function __construct() {
        $this->smtp = new SMTPConnection('smtp.example.com', 587);
        $this->smtp->authenticate('user@example.com', 'password');
    }
    
    public function send(string $recipient, string $subject, string $message): bool {
        return $this->smtp->sendMail($recipient, $subject, $message);
    }
    
    public function sendBulk(array $recipients, string $subject, string $message): array {
        $results = [];
        foreach($recipients as $recipient) {
            $results[$recipient] = $this->send($recipient, $subject, $message);
        }
        return $results;
    }
    
    public function schedule(string $recipient, string $subject, string $message, DateTime $when): bool {
        $db = new PDO('mysql:host=localhost;dbname=notifications','root','');
        $stmt = $db->prepare('INSERT INTO scheduled_emails (recipient,subject,message,send_at) VALUES (?,?,?,?)');
        return $stmt->execute([$recipient, $subject, $message, $when->format('Y-m-d H:i:s')]);
    }
    
    public function getDeliveryStatus(string $messageId): string {
        return $this->smtp->checkStatus($messageId);
    }
    
    public function cancelScheduled(string $messageId): bool {
        $db = new PDO('mysql:host=localhost;dbname=notifications','root','');
        $stmt = $db->prepare('DELETE FROM scheduled_emails WHERE id = ?');
        return $stmt->execute([$messageId]);
    }
}

class SMSNotifier implements Notifier {
    private TwilioClient $client;
    
    public function __construct() {
        $this->client = new TwilioClient('account_sid', 'auth_token');
    }
    
    public function send(string $recipient, string $subject, string $message): bool {
        return $this->client->sendSMS($recipient, $message);
    }
    
    public function sendBulk(array $recipients, string $subject, string $message): array {
        throw new Exception("SMS bulk sending not supported by Twilio API");
    }
    
    public function schedule(string $recipient, string $subject, string $message, DateTime $when): bool {
        throw new Exception("SMS scheduling not implemented");
    }
    
    public function getDeliveryStatus(string $messageId): string {
        return $this->client->getMessageStatus($messageId);
    }
    
    public function cancelScheduled(string $messageId): bool {
        throw new Exception("Cannot cancel SMS");
    }
}

class PushNotifier implements Notifier {
    private FirebaseClient $firebase;
    
    public function __construct() {
        $this->firebase = new FirebaseClient('firebase_key');
    }
    
    public function send(string $recipient, string $subject, string $message): bool {
        return $this->firebase->sendNotification($recipient, $subject, $message);
    }
    
    public function sendBulk(array $recipients, string $subject, string $message): array {
        return $this->firebase->sendToMultiple($recipients, $subject, $message);
    }
    
    public function schedule(string $recipient, string $subject, string $message, DateTime $when): bool {
        throw new Exception("Push notifications don't support scheduling");
    }
    
    public function getDeliveryStatus(string $messageId): string {
        throw new Exception("Firebase doesn't provide delivery status");
    }
    
    public function cancelScheduled(string $messageId): bool {
        throw new Exception("Cannot cancel push notifications");
    }
}

class NotificationService {
    public function notify(User $user, string $subject, string $message): bool {
        $preference = $user->getNotificationPreference();
        
        if($preference === 'email') {
            $notifier = new EmailNotifier();
            return $notifier->send($user->getEmail(), $subject, $message);
        } elseif($preference === 'sms') {
            $notifier = new SMSNotifier();
            return $notifier->send($user->getPhone(), $subject, $message);
        } elseif($preference === 'push') {
            $notifier = new PushNotifier();
            return $notifier->send($user->getDeviceToken(), $subject, $message);
        }
        
        return false;
    }
}

class User {
    public function getNotificationPreference(): string { return 'email'; }
    public function getEmail(): string { return 'user@example.com'; }
    public function getPhone(): string { return '+33612345678'; }
    public function getDeviceToken(): string { return 'device_token_123'; }
}

class SMTPConnection {
    public function __construct(string $host, int $port) { }
    public function authenticate(string $user, string $pass): void { }
    public function sendMail(string $to, string $subject, string $body): bool { return true; }
    public function checkStatus(string $id): string { return 'delivered'; }
}

class TwilioClient {
    public function __construct(string $sid, string $token) { }
    public function sendSMS(string $to, string $message): bool { return true; }
    public function getMessageStatus(string $id): string { return 'sent'; }
}

class FirebaseClient {
    public function __construct(string $key) { }
    public function sendNotification(string $token, string $title, string $body): bool { return true; }
    public function sendToMultiple(array $tokens, string $title, string $body): array { return []; }
}

/*
=== USER STORIES ===

US1: En tant que product owner, je veux ajouter Slack comme canal de notification.

US2: En tant que développeur, je veux tester la logique de NotificationService 
     sans dépendre des vraies APIs (Twilio, Firebase, SMTP).

US3: En tant que product manager, je veux que les utilisateurs puissent choisir 
     plusieurs canaux de notification simultanément (email + push par exemple).

US4: En tant que développeur, je veux créer un nouveau notifier Discord sans que 
     toutes les méthodes de l'interface soient obligatoires.

US5: En tant que compliance officer, je veux tracer toutes les notifications 
     envoyées dans un système centralisé, quel que soit le canal utilisé.
*/