<?php

namespace EasyLinkAutomations\Triggers\Core;

if (!defined('ABSPATH')) {
    exit;
}

use EasyLinkAutomations\Triggers\BaseTrigger;

/**
 * Trigger that fires when an HTTP request is received at the automation's unique webhook URL.
 */
final class IncomingWebhookTrigger extends BaseTrigger
{
    /** Transient TTL in seconds for the listening flag. */
    private const LISTEN_TTL = 5 * MINUTE_IN_SECONDS;

    /** Transient TTL in seconds for captured test data. */
    private const DATA_TTL = 2 * MINUTE_IN_SECONDS;

    public static function getSlug(): string
    {
        return 'incoming-webhook';
    }

    public static function getName(): string
    {
        return __('Incoming Webhook', 'easylink-automations');
    }

    public static function getDescription(): string
    {
        return __('Fires when an HTTP request is received at the automation\'s unique webhook URL.', 'easylink-automations');
    }

    public static function getCategory(): string
    {
        return '_uncategorized';
    }

    public static function getIcon(): string
    {
        return 'api';
    }

    public static function getOutputFields(): array
    {
        return [
            'token'          => ['label' => 'Webhook Token', 'type' => 'string'],
            'request_method' => ['label' => 'Request Method', 'type' => 'string'],
            'content_type'   => ['label' => 'Content Type', 'type' => 'string'],
        ];
    }

    public static function getConfigFields(): array
    {
        return [
            'token' => [
                'label' => 'Webhook Token',
                'type'  => 'hidden',
            ],
            'webhook_url' => [
                'label'       => 'Webhook URL',
                'type'        => 'webhook_url',
                'url_pattern' => get_site_url() . '/wp-json/easylink-automations/v1/webhook/{token}',
                'token_field' => 'token',
            ],
            'http_method' => [
                'label'   => 'HTTP Method',
                'type'    => 'select',
                'options' => [
                    ['value' => 'GET,POST', 'label' => 'GET & POST'],
                    ['value' => 'POST',     'label' => 'POST only'],
                    ['value' => 'GET',      'label' => 'GET only'],
                ],
                'default' => 'GET,POST',
            ],
        ];
    }

    public function register(callable $callback): void
    {
        $this->callback = $callback;

        add_action('rest_api_init', function (): void {
            // Live webhook endpoint
            register_rest_route('easylink-automations/v1', '/webhook/(?P<token>[a-zA-Z0-9_\-]+)', [
                'methods'             => 'GET, POST',
                'callback'            => [$this, 'handleRequest'],
                'permission_callback' => '__return_true',
            ]);

            // Test: activate listening mode
            register_rest_route('easylink-automations/v1', '/webhook-test/(?P<token>[a-zA-Z0-9_\-]+)/start', [
                'methods'             => 'POST',
                'callback'            => [$this, 'handleTestStart'],
                'permission_callback' => fn () => current_user_can('manage_options'),
            ]);

            // Test: poll for captured data
            register_rest_route('easylink-automations/v1', '/webhook-test/(?P<token>[a-zA-Z0-9_\-]+)/poll', [
                'methods'             => 'GET',
                'callback'            => [$this, 'handleTestPoll'],
                'permission_callback' => fn () => current_user_can('manage_options'),
            ]);

            // Test: stop listening
            register_rest_route('easylink-automations/v1', '/webhook-test/(?P<token>[a-zA-Z0-9_\-]+)/stop', [
                'methods'             => 'POST',
                'callback'            => [$this, 'handleTestStop'],
                'permission_callback' => fn () => current_user_can('manage_options'),
            ]);
        });
    }

    public function handle(mixed ...$args): array
    {
        /** @var \WP_REST_Request $request */
        $request     = $args[0];
        $token       = (string) ($request->get_param('token') ?? '');
        $method      = $request->get_method();
        $contentType = $request->get_content_type()['value'] ?? '';

        $body = [];
        if (str_contains($contentType, 'application/json')) {
            $parsed = $request->get_json_params();
            if (is_array($parsed)) {
                $body = $parsed;
            }
        } else {
            $params = $request->get_body_params();
            if (is_array($params)) {
                $body = $params;
            }
        }

        $query = $request->get_query_params();
        unset($query['token']);

        return array_merge([
            'token'          => $token,
            'request_method' => $method,
            'content_type'   => $contentType,
        ], $query, $body);
    }

    /**
     * Handle a live incoming webhook request.
     *
     * @internal Called by WP REST API — not part of the trigger interface contract.
     */
    public function handleRequest(\WP_REST_Request $request): \WP_REST_Response
    {
        $data  = $this->handle($request);
        $token = $data['token'];

        // If the admin is actively listening for a test, capture this request.
        if (get_transient($this->listenKey($token))) {
            set_transient($this->dataKey($token), $data, self::DATA_TTL);
            delete_transient($this->listenKey($token));
        }

        $this->fire($data);

        return new \WP_REST_Response(['success' => true], 200);
    }

    /**
     * Activate test-listening mode for a token.
     */
    public function handleTestStart(\WP_REST_Request $request): \WP_REST_Response
    {
        $token = (string) ($request->get_param('token') ?? '');

        // Clear any stale captured data from a previous test
        delete_transient($this->dataKey($token));

        set_transient($this->listenKey($token), true, self::LISTEN_TTL);

        return new \WP_REST_Response(['status' => 'listening'], 200);
    }

    /**
     * Poll for test data captured since listening started.
     */
    public function handleTestPoll(\WP_REST_Request $request): \WP_REST_Response
    {
        $token = (string) ($request->get_param('token') ?? '');
        $data  = get_transient($this->dataKey($token));

        if ($data !== false) {
            delete_transient($this->dataKey($token));

            // Build a clean field list (exclude internal meta keys)
            $fields = array_filter(
                $data,
                fn ($key) => !in_array($key, ['token', 'request_method', 'content_type'], true),
                ARRAY_FILTER_USE_KEY
            );

            // Flatten nested objects to dot-notation (e.g. user.name) so the
            // UI can display usable {{user.name}} placeholders and all values
            // are scalars (not [object Object] in JavaScript).
            $fields = static::flattenFields($fields);

            return new \WP_REST_Response([
                'status' => 'received',
                'data'   => $data,
                'fields' => $fields,
            ], 200);
        }

        return new \WP_REST_Response(['status' => 'waiting'], 200);
    }

    /**
     * Stop test-listening mode.
     */
    public function handleTestStop(\WP_REST_Request $request): \WP_REST_Response
    {
        $token = (string) ($request->get_param('token') ?? '');
        delete_transient($this->listenKey($token));

        return new \WP_REST_Response(['status' => 'stopped'], 200);
    }

    // -------------------------------------------------------------------------
    // Helpers
    // -------------------------------------------------------------------------

    private function listenKey(string $token): string
    {
        return 'easylink_automations_wb_listen_' . $token;
    }

    private function dataKey(string $token): string
    {
        return 'easylink_automations_wb_data_' . $token;
    }
}
