<?php
require_once __DIR__ . '/../Core.php';
require_once __DIR__ . '/../models/Message.php';
require_once __DIR__ . '/../models/Contact.php';
require_once __DIR__ . '/../models/Suppression.php';
require_once __DIR__ . '/../services.php';

class WebhookController extends Controller
{
    private function verifyTwilio(): void
    {
        $service = new TwilioService();
        $signature = $_SERVER['HTTP_X_TWILIO_SIGNATURE'] ?? null;
        if (!$service->validateRequest(get_current_request_url(), $_POST, $signature)) {
            http_response_code(403);
            exit('Invalid Twilio signature.');
        }
    }

    public function twilio(): void
    {
        $this->verifyTwilio();
        $from = $_POST['From'] ?? '';
        $to = $_POST['To'] ?? '';
        $body = $_POST['Body'] ?? '';
        $sid = $_POST['MessageSid'] ?? null;
        $mediaUrl = !empty($_POST['MediaUrl0']) ? $_POST['MediaUrl0'] : null;
        $contactModel = new Contact();
        $contact = $contactModel->findByAddress('sms', $from);
        if (in_array(strtoupper(trim($body)), ['STOP', 'STOPALL', 'UNSUBSCRIBE', 'CANCEL', 'END', 'QUIT'], true)) {
            $contactModel->setUnsubscribed('sms', $from, true);
            (new Suppression())->add('sms', $from, 'unsubscribe', $contact['id'] ?? null, null);
        }
        $messageId = (new Message())->create([
            'contact_id' => $contact['id'] ?? null,
            'channel' => 'sms',
            'direction' => 'inbound',
            'body' => $body,
            'from_address' => $from,
            'to_address' => $to,
            'status' => 'received',
            'external_id' => $sid,
            'media_url' => $mediaUrl,
            'metadata_json' => json_encode($_POST),
        ]);
        audit_log('webhook.twilio.inbound', 'message', $messageId, ['from' => $from]);
        header('Content-Type: text/xml');
        echo '<?xml version="1.0" encoding="UTF-8"?><Response></Response>';
        exit;
    }

    public function twilioStatus(): void
    {
        $this->verifyTwilio();
        $sid = $_POST['MessageSid'] ?? '';
        $status = $_POST['MessageStatus'] ?? 'updated';
        if ($sid) {
            $messageModel = new Message();
            $messageModel->updateStatusByExternalId($sid, $status, json_encode($_POST));
            if (in_array($status, ['failed', 'undelivered'], true)) {
                $message = Database::connection()->prepare('SELECT * FROM messages WHERE external_id = ? LIMIT 1');
                $message->execute([$sid]);
                $row = $message->fetch();
                if ($row && !empty($row['to_address'])) {
                    (new Suppression())->add('sms', $row['to_address'], 'delivery_failure', $row['contact_id'] ?: null, $row['id']);
                }
            }
        }
        http_response_code(204);
        exit;
    }


    private function verifySendGridWebhook(): void
    {
        $token = trim((string)(effective_mail_config()['sendgrid_webhook_token'] ?? ''));
        if ($token === '') {
            return;
        }
        $provided = trim((string)($_GET['token'] ?? $_SERVER['HTTP_X_AIO_WEBHOOK_TOKEN'] ?? ''));
        if ($provided === '' || !hash_equals($token, $provided)) {
            http_response_code(403);
            exit('Invalid SendGrid webhook token.');
        }
    }

    private function findMessageForSendGridEvent(array $event): ?array
    {
        $externalId = '';
        if (!empty($event['custom_args']) && is_array($event['custom_args']) && !empty($event['custom_args']['aio_message_external_id'])) {
            $externalId = trim((string)$event['custom_args']['aio_message_external_id']);
        } elseif (!empty($event['aio_message_external_id'])) {
            $externalId = trim((string)$event['aio_message_external_id']);
        } elseif (!empty($event['smtp-id'])) {
            $externalId = trim((string)$event['smtp-id']);
        }
        if ($externalId === '') {
            return null;
        }
        $stmt = Database::connection()->prepare('SELECT * FROM messages WHERE external_id = ? LIMIT 1');
        $stmt->execute([$externalId]);
        return $stmt->fetch() ?: null;
    }

    private function applySendGridEvent(array $event): void
    {
        $message = $this->findMessageForSendGridEvent($event);
        if (!$message) {
            return;
        }

        $rawType = strtolower(trim((string)($event['event'] ?? '')));
        $reason = trim((string)($event['reason'] ?? $event['response'] ?? ($rawType !== '' ? $rawType : 'sendgrid_event')));
        $eventType = 'other';
        $status = null;
        $eventValue = $reason;

        switch ($rawType) {
            case 'processed':
                $status = 'sent';
                $eventValue = 'processed';
                break;
            case 'deferred':
                $status = 'queued';
                $eventValue = $reason !== '' ? $reason : 'deferred';
                break;
            case 'delivered':
                $eventType = 'delivery';
                $status = 'delivered';
                $eventValue = 'delivered';
                break;
            case 'bounce':
            case 'blocked':
            case 'dropped':
            case 'spam report':
                $eventType = 'bounce';
                $status = 'bounced';
                break;
            case 'open':
                $eventType = 'open';
                $eventValue = $reason !== '' ? $reason : 'open';
                break;
            case 'click':
                $eventType = 'click';
                $eventValue = trim((string)($event['url'] ?? $reason));
                break;
            case 'unsubscribe':
            case 'group_unsubscribe':
            case 'group_resubscribe':
                $eventValue = $rawType;
                break;
            default:
                $eventValue = $rawType !== '' ? $rawType : $reason;
                break;
        }

        $messageModel = new Message();
        $metadataJson = json_encode($event);
        if ($status !== null) {
            $messageModel->updateStatus((int)$message['id'], $status, $metadataJson);
        }
        $messageModel->addEvent((int)$message['id'], $eventType, $eventValue, $metadataJson);

        $address = (string)($event['email'] ?? $message['to_address'] ?? '');
        if (in_array($rawType, ['bounce', 'blocked', 'dropped', 'spam report'], true) && $address !== '') {
            (new Suppression())->add('email', $address, $rawType . ':' . ($reason !== '' ? $reason : 'sendgrid'), $message['contact_id'] ?: null, (int)$message['id']);
            (new Contact())->setUnsubscribed('email', $address, true);
        }
        if (in_array($rawType, ['unsubscribe', 'group_unsubscribe'], true) && $address !== '') {
            (new Suppression())->add('email', $address, $rawType, $message['contact_id'] ?: null, (int)$message['id']);
            (new Contact())->setUnsubscribed('email', $address, true);
        }
    }

    public function emailEvent(): void
    {
        $provider = strtolower(trim((string)($_REQUEST['provider'] ?? '')));
        if ($provider === 'sendgrid') {
            $this->verifySendGridWebhook();
            $payload = json_decode((string)file_get_contents('php://input'), true);
            if ($payload === null) {
                http_response_code(400);
                exit('Invalid SendGrid event payload.');
            }
            $isList = is_array($payload) && ($payload === [] || array_keys($payload) === range(0, count($payload) - 1));
            $events = $isList ? $payload : [$payload];
            foreach ($events as $event) {
                if (is_array($event)) {
                    $this->applySendGridEvent($event);
                }
            }
            http_response_code(204);
            exit;
        }

        $messageId = (int)($_REQUEST['message_id'] ?? 0);
        $eventType = strtolower(trim((string)($_REQUEST['event'] ?? '')));
        $reason = trim((string)($_REQUEST['reason'] ?? 'provider_event'));
        if ($messageId && in_array($eventType, ['bounce', 'complaint', 'blocked'], true)) {
            $messageModel = new Message();
            $message = $messageModel->find($messageId);
            if ($message) {
                $messageModel->updateStatus($messageId, 'bounced', json_encode($_REQUEST));
                $messageModel->addEvent($messageId, $eventType, $reason, json_encode($_REQUEST));
                if (!empty($message['to_address'])) {
                    (new Suppression())->add('email', $message['to_address'], $eventType . ':' . $reason, $message['contact_id'] ?: null, $messageId);
                    (new Contact())->setUnsubscribed('email', $message['to_address'], true);
                }
                audit_log('webhook.email_event', 'message', $messageId, ['event' => $eventType, 'reason' => $reason]);
            }
        }
        http_response_code(204);
        exit;
    }

    public function trackOpen(): void
    {
        $messageId = (int)($_GET['message_id'] ?? 0);
        if ($messageId) {
            (new Message())->addEvent($messageId, 'open', null, json_encode(['ip' => $_SERVER['REMOTE_ADDR'] ?? null, 'ua' => $_SERVER['HTTP_USER_AGENT'] ?? null]));
        }
        header('Content-Type: image/gif');
        echo base64_decode('R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==');
        exit;
    }

    public function trackClick(): void
    {
        $messageId = (int)($_GET['message_id'] ?? 0);
        $url = $_GET['url'] ?? '';
        if ($messageId) {
            (new Message())->addEvent($messageId, 'click', $url, json_encode(['ip' => $_SERVER['REMOTE_ADDR'] ?? null]));
        }
        redirect_url($url ?: '/');
    }
}
