<?php

namespace FleekDash\Integrations\Core;

defined('ABSPATH') || exit;

if (!function_exists('fleekdash_log_error')) {
    require_once FLEEKDASH_PLUGIN_DIR . 'includes/Core/debug-helpers.php';
}

/**
 * Webhook Manager for Integrations
 *
 * Handles incoming and outgoing webhooks
 */
class WebhookManager
{
    private const REST_API_NAMESPACE = 'fleekdash/v1';

    private Container $container;
    private Cache $cache;
    private EventDispatcher $events;
    private WebhookOutgoingProcessor $outgoingProcessor;
    private WebhookIncomingAuthenticator $incomingAuthenticator;
    private array $endpoints = [];
    private array $subscriptions = [];

    public function __construct(Container $container, Cache $cache, EventDispatcher $events)
    {
        $this->container = $container;
        $this->cache = $cache;
        $this->events = $events;
        $this->outgoingProcessor = new WebhookOutgoingProcessor();
        $this->incomingAuthenticator = new WebhookIncomingAuthenticator();
        $this->registerHooks();
    }

    /**
     * Register WordPress hooks
     */
    private function registerHooks(): void
    {
        add_action('rest_api_init', [$this, 'registerRestRoutes']);
        add_action('init', [$this, 'processWebhookQueue']);
    }

    /**
     * Register a webhook endpoint
     */
    public function registerEndpoint(string $integrationId, string $endpoint, array $config): void
    {
        $defaults = [
            'methods' => ['POST'],
            'callback' => null,
            'authentication' => 'signature', // signature, token, none
            'secret' => '',
            'events' => [],
            'rate_limit' => 100, // per hour
            'timeout' => 30
        ];

        $this->endpoints[$integrationId][$endpoint] = array_merge($defaults, $config);
    }

    /**
     * Register webhook subscription (outgoing)
     */
    public function subscribe(string $event, string $url, array $config = []): string
    {
        $id = wp_generate_uuid4();

        $defaults = [
            'url' => $url,
            'events' => [$event],
            'secret' => wp_generate_password(32, false),
            'retry_count' => 3,
            'timeout' => 30,
            'headers' => [],
            'active' => true
        ];

        $this->subscriptions[$id] = array_merge($defaults, $config);
        $this->saveSubscriptions();

        return $id;
    }

    /**
     * Unsubscribe from webhook
     */
    public function unsubscribe(string $subscriptionId): bool
    {
        if (isset($this->subscriptions[$subscriptionId])) {
            unset($this->subscriptions[$subscriptionId]);
            $this->saveSubscriptions();
            return true;
        }
        return false;
    }

    /**
     * Trigger outgoing webhooks for an event
     */
    public function trigger(string $event, array $data = []): void
    {
        $subscriptions = $this->getSubscriptionsForEvent($event);

        foreach ($subscriptions as $id => $subscription) {
            if (!$subscription['active']) {
                continue;
            }

            $this->outgoingProcessor->enqueue($id, $event, $data);
        }
    }

    /**
     * Process webhook queue
     */
    public function processWebhookQueue(): void
    {
        $this->outgoingProcessor->processPending($this->subscriptions);
    }

    /**
     * Handle incoming webhook
     */
    public function handleIncomingWebhook(\WP_REST_Request $request): \WP_REST_Response|\WP_Error
    {
        $integration = (string) $request->get_param('integration');
        $endpoint = (string) $request->get_param('endpoint');
        $resolved = null;

        if (!isset($this->endpoints[$integration][$endpoint])) {
            $resolved = new \WP_Error('endpoint_not_found', 'Webhook endpoint not found', ['status' => 404]);
        } elseif (!$this->checkRateLimit($integration, $endpoint, (int) $this->endpoints[$integration][$endpoint]['rate_limit'])) {
            $resolved = new \WP_Error('rate_limit_exceeded', 'Rate limit exceeded', ['status' => 429]);
        } else {
            $config = $this->endpoints[$integration][$endpoint];
            $auth = $this->incomingAuthenticator->authenticate($request, $config);
            if (is_wp_error($auth)) {
                $resolved = $auth;
            } elseif (is_callable($config['callback'])) {
                try {
                    $result = call_user_func($config['callback'], $request);
                    $this->logWebhookReceived($integration, $endpoint, $request);
                    $resolved = rest_ensure_response($result);
                } catch (\Exception $e) {
                    fleekdash_log_error('[WebhookManager] Webhook error: ' . $e->getMessage());
                    $resolved = new \WP_Error('webhook_error', 'Webhook processing failed', ['status' => 500]);
                }
            } else {
                $resolved = rest_ensure_response(['received' => true]);
            }
        }

        if (!$resolved instanceof \WP_REST_Response && !is_wp_error($resolved)) {
            $resolved = new \WP_Error('webhook_internal', 'Unable to process webhook', ['status' => 500]);
        }

        return $resolved;
    }

    /**
     * Check rate limit
     */
    private function checkRateLimit(string $integration, string $endpoint, int $limit): bool
    {
        $key = "webhook_rate_{$integration}_{$endpoint}";
        $requests = $this->cache->get($key, []);
        if (!is_array($requests)) {
            $requests = [];
        }

        $hour = date('Y-m-d H');
        $requests[$hour] = ($requests[$hour] ?? 0) + 1;

        // Clean old entries (keep last 2 hours)
        $requests = array_slice($requests, -2, null, true);

        $this->cache->set($key, $requests, 7200); // 2 hours

        return $requests[$hour] <= $limit;
    }

    /**
     * Get subscriptions for event
     */
    private function getSubscriptionsForEvent(string $event): array
    {
        return array_filter($this->subscriptions, function ($subscription) use ($event) {
            return in_array($event, $subscription['events']) || in_array('*', $subscription['events']);
        });
    }

    /**
     * Save subscriptions to database
     */
    private function saveSubscriptions(): void
    {
        update_option('fleekdash_webhook_subscriptions', $this->subscriptions);
    }

    /**
     * Load subscriptions from database
     */
    private function loadSubscriptions(): void
    {
        $subscriptions = get_option('fleekdash_webhook_subscriptions', []);
        $this->subscriptions = is_array($subscriptions) ? $subscriptions : [];
    }

    /**
     * Log incoming webhook
     */
    private function logWebhookReceived(string $integration, string $endpoint, \WP_REST_Request $request): void
    {
        $log = [
            'type' => 'received',
            'integration' => $integration,
            'endpoint' => $endpoint,
            'method' => $request->get_method(),
            'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
            'timestamp' => current_time('mysql'),
        ];

        WebhookLogRepository::append($log);
    }

    /**
     * Register REST API routes
     */
    public function registerRestRoutes(): void
    {
        $ns = self::REST_API_NAMESPACE;

        register_rest_route($ns, '/webhooks/(?P<integration>[a-zA-Z0-9-_]+)/(?P<endpoint>[a-zA-Z0-9-_]+)', [
            'methods' => ['POST', 'GET', 'PUT', 'DELETE'],
            'callback' => [$this, 'handleIncomingWebhook'],
            'permission_callback' => [$this, 'allowIncomingWebhookRequest'],
        ]);

        register_rest_route($ns, '/webhooks/logs', [
            'methods' => 'GET',
            'callback' => [$this, 'restGetLogs'],
            'permission_callback' => function () {
                return current_user_can('manage_options');
            },
        ]);

        register_rest_route($ns, '/webhooks/subscriptions', [
            'methods' => 'GET',
            'callback' => [$this, 'restGetSubscriptions'],
            'permission_callback' => function () {
                return current_user_can('manage_options');
            },
        ]);
    }

    public function allowIncomingWebhookRequest(): bool
    {
        return true;
    }

    /**
     * REST: Get webhook logs
     */
    public function restGetLogs(\WP_REST_Request $request): \WP_REST_Response
    {
        $logs = get_option('fleekdash_webhook_logs', []);
        $logs = is_array($logs) ? $logs : [];
        $limit = $request->get_param('limit') ?: 50;

        $logs = array_slice($logs, -$limit);

        return rest_ensure_response([
            'success' => true,
            'data' => $logs
        ]);
    }

    /**
     * REST: Get subscriptions
     */
    public function restGetSubscriptions(): \WP_REST_Response
    {
        return rest_ensure_response([
            'success' => true,
            'data' => $this->subscriptions
        ]);
    }

    /**
     * Initialize the webhook manager
     */
    public function init(): void
    {
        $this->loadSubscriptions();

        // Add cron job for processing webhooks
        if (!wp_next_scheduled('fleekdash_process_webhooks')) {
            wp_schedule_event(time(), 'hourly', 'fleekdash_process_webhooks');
        }

        add_action('fleekdash_process_webhooks', [$this, 'processWebhookQueue']);
    }
}
