<?php

class hysndrEspEventAdapter implements EspEventAdapterInterface
{
    public function verifyRequest(array $server, string $rawBody): bool
    {
        $expected = trim((string)mh_cfg('MAILBEEZ_MLBZ_ESP_WEBHOOK_TOKEN', ''));
        if ($expected === '') {
            return true;
        }

        $token = '';
        if (isset($server['HTTP_X_MAILBEEZ_TOKEN'])) {
            $token = (string)$server['HTTP_X_MAILBEEZ_TOKEN'];
        }
        if ($token == '' && isset($_GET['token'])) {
            $token = (string)$_GET['token'];
        }
        return ($expected != '' && hash_equals($expected, $token));
    }

    public function normalizeEvent(array $payload): array
    {
        $eventType = $this->normalizeEventType($this->pick($payload, array('event', 'type', 'name', 'event_type')));
        $eventTime = $this->normalizeEventTime($this->pick($payload, array('event_time', 'timestamp', 'created_at', 'time')));

        $recipientEmail = (string)$this->pick($payload, array('recipient_email', 'email', 'recipient', 'to'));
        $rawHeaders = $this->pick($payload, array('headers', 'message_headers'));
        $headerMessageId = $this->headerValue($rawHeaders, 'X-MailBeez-Message-ID');
        return array(
            'provider' => 'hysndr',
            'provider_event_id' => $this->pick($payload, array('event_id', 'id', 'provider_event_id')),
            'provider_message_id' => $this->pick($payload, array('provider_message_id', 'message_id', 'smtp_message_id')),
            'mailbeez_message_id' => $this->firstNotEmpty(array(
                $this->pick($payload, array('mailbeez_message_id', 'message.mailbeez_message_id', 'tags.mailbeez_message_id')),
                $headerMessageId,
                $this->pick($payload, array('headers.X-MailBeez-Message-ID')),
                $this->pick($payload, array('headers.x-mailbeez-message-id')),
                $this->pick($payload, array('message_headers.X-MailBeez-Message-ID')),
                $this->pick($payload, array('message_headers.x-mailbeez-message-id')),
            )),
            'event_type' => $eventType,
            'event_time' => $eventTime,
            'recipient_email' => $recipientEmail,
            'severity' => $this->severityFor($eventType),
            'bounce_type' => ($eventType == 'hard_bounce') ? 'H' : (($eventType == 'soft_bounce') ? 'S' : null),
            'bounce_code' => (string)$this->pick($payload, array('bounce_code', 'smtp_code', 'code')),
            'reason' => (string)$this->pick($payload, array('reason', 'description', 'error')),
            'url' => (string)$this->pick($payload, array('url', 'click_url')),
            'attempt_no' => (int)$this->pick($payload, array('attempt_no', 'attempt', 'attempt_number')),
            'correlation_confidence' => 'unknown',
            'raw_payload' => $payload,
            'headers' => is_array($rawHeaders) ? $rawHeaders : array(),
            'tags' => (array)$this->pick($payload, array('tags')),
        );
    }

    public function connectionTest(): array
    {
        return array('result' => true, 'provider' => 'hysndr', 'message' => 'ok');
    }

    private function pick(array $payload, array $keys)
    {
        foreach ($keys as $key) {
            if (strpos($key, '.') === false) {
                if (isset($payload[$key])) {
                    return $payload[$key];
                }
                continue;
            }
            $value = $this->pickPath($payload, $key);
            if ($value !== null) {
                return $value;
            }
        }
        return null;
    }

    private function pickPath(array $payload, $path)
    {
        $segments = explode('.', $path);
        $current = $payload;
        foreach ($segments as $segment) {
            if (!is_array($current) || !array_key_exists($segment, $current)) {
                return null;
            }
            $current = $current[$segment];
        }
        return $current;
    }

    private function normalizeEventType($raw)
    {
        $value = strtolower(trim((string)$raw));
        $mapping = array(
            'queued' => 'queued',
            'attempt' => 'attempt',
            'sending' => 'attempt',
            'sent' => 'sent',
            'delivered' => 'sent',
            'soft_bounce' => 'soft_bounce',
            'bounce_soft' => 'soft_bounce',
            'hard_bounce' => 'hard_bounce',
            'bounce_hard' => 'hard_bounce',
            'complaint' => 'complaint',
            'spam' => 'complaint',
            'unsubscribe' => 'unsubscribe',
            'open' => 'open',
            'opened' => 'open',
            'click' => 'click',
            'clicked' => 'click',
        );
        return isset($mapping[$value]) ? $mapping[$value] : 'unknown';
    }

    private function severityFor($eventType)
    {
        switch ($eventType) {
            case 'hard_bounce':
            case 'complaint':
                return 'failed';
            case 'soft_bounce':
                return 'warning';
            case 'open':
            case 'click':
                return 'engaged';
            default:
                return 'info';
        }
    }

    private function headerValue($headers, $name)
    {
        if (!is_array($headers)) {
            return null;
        }

        $target = strtolower($name);
        foreach ($headers as $key => $value) {
            if (is_array($value) && array_key_exists('key', $value)) {
                $headerKey = strtolower(trim((string)$value['key']));
                if ($headerKey === $target) {
                    return array_key_exists('value', $value) ? $value['value'] : null;
                }
                continue;
            }

            if (strtolower((string)$key) === $target) {
                return $value;
            }
        }
        return null;
    }

    private function normalizeEventTime($eventTime)
    {
        if ($eventTime === null || $eventTime === '') {
            return date('Y-m-d H:i:s');
        }

        $timestamp = strtotime(str_replace('T', ' ', (string)$eventTime));
        if ($timestamp === false || $timestamp <= 0) {
            return date('Y-m-d H:i:s');
        }

        return date('Y-m-d H:i:s', $timestamp);
    }

    private function firstNotEmpty(array $values)
    {
        foreach ($values as $value) {
            if ($value !== null && $value !== '') {
                return $value;
            }
        }
        return null;
    }
}
