<?php
/**
 * TrackSharp GA4 Client
 * 
 * Handles GA4 Measurement Protocol API communication.
 *
 * @package TrackSharp
 */

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

class TrackSharp_GA4
{
    /**
     * Send event to GA4 Measurement Protocol.
     *
     * @param array $data Event payload.
     * @return array Response with 'ok', 'status', 'body', 'validate' keys.
     */
    public function send($data)
    {
        $mid = trim(get_option('tracksharp_ga4_measurement_id', ''));
        $sec = trim(get_option('tracksharp_ga4_api_secret', ''));

        if (!$mid || !$sec) {
            return ['ok' => false, 'reason' => 'ga4 not configured'];
        }

        // Synthesize client_id if not provided
        if (empty($data['client_id'])) {
            $data['client_id'] = time() . '.' . wp_rand(1000000000, 2147483647);
        }

        // Ensure an event exists
        if (empty($data['events']) || !is_array($data['events'])) {
            return ['ok' => false, 'reason' => 'no events'];
        }

        // Add engagement_time_msec
        if (empty($data['events'][0]['params']['engagement_time_msec'])) {
            $data['events'][0]['params']['engagement_time_msec'] = 1;
        }

        // Add timestamp
        if (empty($data['events'][0]['timestamp_micros'])) {
            $data['events'][0]['timestamp_micros'] = (int) (microtime(true) * 1e6);
        }

        // Remove top-level debug_mode
        unset($data['debug_mode']);

        // Extract HTTP context for sGTM-style header forwarding and remove from payload before encoding.
        // Forwarding the original browser cookies (_ga, _ga_*), IP, and UA as HTTP headers mirrors
        // how sGTM proxies browser hits to GA4, which is required for session-based ads attribution.
        $http_context = [];
        if (isset($data['_http_context']) && is_array($data['_http_context'])) {
            $http_context = $data['_http_context'];
            unset($data['_http_context']);
        }

        $host = apply_filters('tracksharp_ga4_host', 'https://region1.google-analytics.com/mp/collect');
        $endpoint = $host . '?measurement_id=' . rawurlencode($mid) . '&api_secret=' . rawurlencode($sec);

        // Validate first for human-readable errors
        $validate = $this->validate($data, $mid, $sec);

        $headers = ['Content-Type' => 'application/json'];
        if (!empty($http_context['cookie'])) {
            $headers['Cookie'] = (string) $http_context['cookie'];
        }
        if (!empty($http_context['client_ip'])) {
            $headers['X-Forwarded-For'] = (string) $http_context['client_ip'];
        }
        if (!empty($http_context['user_agent'])) {
            $headers['User-Agent'] = (string) $http_context['user_agent'];
        }

        $args = [
            'headers' => $headers,
            'body' => wp_json_encode($data),
            'timeout' => 8,
        ];

        $res = wp_remote_post($endpoint, $args);

        // Retry on failure
        if (is_wp_error($res) || wp_remote_retrieve_response_code($res) >= 500) {
            usleep(250000); // 250ms
            $res = wp_remote_post($endpoint, $args);
        }

        if (is_wp_error($res)) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional production logging for API errors
            error_log('[TrackSharp][GA4] error=' . $res->get_error_message());
            return ['ok' => false, 'error' => $res->get_error_message(), 'validate' => $validate];
        }

        $code = wp_remote_retrieve_response_code($res);
        $body = wp_remote_retrieve_body($res);

        // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional production logging for API responses
        error_log('[TrackSharp][GA4] status=' . $code . ' body=' . (string) $body);

        return [
            'ok' => ($code >= 200 && $code < 300),
            'status' => $code,
            'body' => $body,
            'validate' => $validate,
        ];
    }

    /**
     * Validate event against GA4 debug endpoint.
     *
     * @param array  $data Event payload.
     * @param string $mid  Measurement ID.
     * @param string $sec  API secret.
     * @return array Validation response.
     */
    public function validate($data, $mid, $sec)
    {
        try {
            $endpoint = 'https://region1.google-analytics.com/debug/mp/collect'
                . '?measurement_id=' . rawurlencode($mid)
                . '&api_secret=' . rawurlencode($sec);

            $args = [
                'headers' => ['Content-Type' => 'application/json'],
                'body' => wp_json_encode($data),
                'timeout' => 8,
            ];

            $res = wp_remote_post($endpoint, $args);

            if (is_wp_error($res) || wp_remote_retrieve_response_code($res) >= 500) {
                usleep(250000);
                $res = wp_remote_post($endpoint, $args);
            }

            if (is_wp_error($res)) {
                // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional production logging for API errors
                error_log('[TrackSharp][GA4][validate] error=' . $res->get_error_message());
                return ['ok' => false, 'error' => $res->get_error_message()];
            }

            $code = wp_remote_retrieve_response_code($res);
            $body = wp_remote_retrieve_body($res);

            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional production logging for API responses
            error_log('[TrackSharp][GA4][validate] status=' . $code . ' body=' . $body);

            return ['ok' => ($code >= 200 && $code < 300), 'status' => $code, 'body' => $body];
        } catch (\Throwable $e) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Intentional production logging for exceptions
            error_log('[TrackSharp][GA4][validate] exception=' . $e->getMessage());
            return ['ok' => false, 'error' => $e->getMessage()];
        }
    }

    /**
     * Check if GA4 is configured.
     *
     * @return bool True if both measurement ID and API secret are set.
     */
    public function is_configured()
    {
        return !empty(get_option('tracksharp_ga4_measurement_id')) && !empty(get_option('tracksharp_ga4_api_secret'));
    }
}
