<?php
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Webhook Dispatcher
 *
 * Dispatches plugin events to active webhook endpoints.
 *
 * @package    Kitsune_AI_Platform
 * @subpackage Kitsune_AI_Platform/includes
 */
class Kitsune_AI_Platform_Webhook_Dispatcher {

    /**
     * Dispatch an event payload to configured webhook endpoints.
     *
     * @param string $event   Event name.
     * @param array  $payload Event payload.
     * @return array Dispatch results per webhook.
     */
    public static function dispatch($event, $payload = array()) {
        $event = is_scalar($event) ? sanitize_text_field((string) $event) : '';
        if ($event === '') {
            return array();
        }

        if (!is_array($payload)) {
            $payload = array('data' => $payload);
        }

        $webhooks = get_option('kitsune_ai_platform_webhooks', array());
        if (!is_array($webhooks) || empty($webhooks)) {
            return array();
        }

        $timeout = max(1, (int) apply_filters('kitsune_ai_platform_webhook_timeout', 15, $event));
        $base_body = array(
            'event' => $event,
            'timestamp' => current_time('c'),
            'payload' => $payload,
        );

        $results = array();
        foreach ($webhooks as $webhook) {
            if (!self::should_dispatch_webhook($webhook, $event)) {
                continue;
            }

            $url = esc_url_raw($webhook['url'] ?? '');
            if ($url === '') {
                continue;
            }

            $validated_url = self::validate_webhook_url($url);
            if (is_wp_error($validated_url)) {
                $results[] = array(
                    'webhook_id' => intval($webhook['id'] ?? 0),
                    'success' => false,
                    'message' => $validated_url->get_error_message(),
                );
                do_action('kitsune_ai_platform_webhook_failed', $event, $payload, $webhook, $validated_url);
                continue;
            }

            $request_args = array(
                'headers' => array(
                    'Content-Type' => 'application/json',
                ),
                'body' => wp_json_encode($base_body),
                'timeout' => $timeout,
            );

            $request_args = apply_filters(
                'kitsune_ai_platform_webhook_request_args',
                $request_args,
                $event,
                $payload,
                $webhook
            );

            do_action('kitsune_ai_platform_before_webhook_dispatch', $event, $payload, $webhook, $request_args);

            $response = wp_remote_post($validated_url, $request_args);
            if (is_wp_error($response)) {
                do_action('kitsune_ai_platform_webhook_failed', $event, $payload, $webhook, $response);
                $results[] = array(
                    'webhook_id' => intval($webhook['id'] ?? 0),
                    'success' => false,
                    'message' => $response->get_error_message(),
                );
                continue;
            }

            $status_code = (int) wp_remote_retrieve_response_code($response);
            $success = $status_code >= 200 && $status_code < 300;

            if ($success) {
                do_action('kitsune_ai_platform_webhook_dispatched', $event, $payload, $webhook, $response);
            } else {
                do_action('kitsune_ai_platform_webhook_failed', $event, $payload, $webhook, $response);
            }

            $results[] = array(
                'webhook_id' => intval($webhook['id'] ?? 0),
                'success' => $success,
                'status_code' => $status_code,
            );
        }

        return $results;
    }

    /**
     * Validate webhook URL against protocol, host, and IP safety rules.
     *
     * @param string $url URL to validate.
     * @return string|WP_Error
     */
    public static function validate_webhook_url($url) {
        $url = esc_url_raw($url);
        if ($url === '') {
            return new WP_Error('kitsune_ai_platform_invalid_webhook_url', __('Webhook URL is required.', 'kitsune-ai-platform'));
        }

        $parts = wp_parse_url($url);
        if (!is_array($parts)) {
            return new WP_Error('kitsune_ai_platform_invalid_webhook_url', __('Webhook URL is invalid.', 'kitsune-ai-platform'));
        }

        $scheme = strtolower((string) ($parts['scheme'] ?? ''));
        if (!in_array($scheme, array('http', 'https'), true)) {
            return new WP_Error('kitsune_ai_platform_invalid_webhook_scheme', __('Webhook URL must use HTTP or HTTPS.', 'kitsune-ai-platform'));
        }

        $host = strtolower((string) ($parts['host'] ?? ''));
        if ($host === '' || $host === 'localhost' || substr($host, -6) === '.local') {
            return new WP_Error('kitsune_ai_platform_invalid_webhook_host', __('Webhook host is not allowed.', 'kitsune-ai-platform'));
        }

        $allowed_hosts = apply_filters('kitsune_ai_platform_webhook_allowed_hosts', array());
        if (is_array($allowed_hosts) && !empty($allowed_hosts)) {
            $normalized_hosts = array_values(array_filter(array_map('strtolower', array_map('trim', $allowed_hosts))));
            if (!in_array($host, $normalized_hosts, true)) {
                    return new WP_Error('kitsune_ai_platform_webhook_host_not_allowlisted', __('Webhook host is not in the allowlist.', 'kitsune-ai-platform'));
            }
        }

        $ips = self::resolve_host_ips($host);
        if (empty($ips)) {
            return new WP_Error('kitsune_ai_platform_webhook_dns_failed', __('Webhook host could not be resolved.', 'kitsune-ai-platform'));
        }

        foreach ($ips as $ip) {
            if (!self::is_public_ip($ip)) {
                return new WP_Error('kitsune_ai_platform_webhook_private_ip', __('Webhook host resolves to a private or reserved IP.', 'kitsune-ai-platform'));
            }
        }

        return $url;
    }

    /**
     * Resolve IP addresses for webhook host.
     *
     * @param string $host Hostname or IP.
     * @return array
     */
    private static function resolve_host_ips($host) {
        if (filter_var($host, FILTER_VALIDATE_IP)) {
            return array($host);
        }

        $ips = array();

        $a_records = gethostbynamel($host);
        if (is_array($a_records)) {
            $ips = array_merge($ips, $a_records);
        }

        if (function_exists('dns_get_record') && defined('DNS_AAAA')) {
            $aaaa_records = dns_get_record($host, DNS_AAAA);
            if (is_array($aaaa_records)) {
                foreach ($aaaa_records as $record) {
                    if (!empty($record['ipv6']) && is_string($record['ipv6'])) {
                        $ips[] = $record['ipv6'];
                    }
                }
            }
        }

        $ips = array_filter(array_map('trim', $ips), function ($ip) {
            return is_string($ip) && $ip !== '';
        });

        return array_values(array_unique($ips));
    }

    /**
     * Check whether an IP is public routable.
     *
     * @param string $ip IP address.
     * @return bool
     */
    private static function is_public_ip($ip) {
        if (!is_string($ip) || $ip === '') {
            return false;
        }

        return (bool) filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
    }

    /**
     * Check whether a webhook should receive the event.
     *
     * @param mixed  $webhook Webhook config row.
     * @param string $event   Event name.
     * @return bool
     */
    private static function should_dispatch_webhook($webhook, $event) {
        if (!is_array($webhook)) {
            return false;
        }

        if (isset($webhook['active']) && empty($webhook['active'])) {
            return false;
        }

        $events = isset($webhook['events']) && is_array($webhook['events']) ? $webhook['events'] : array();
        return in_array($event, $events, true);
    }
}
