<?php
declare(strict_types=1);
// ==================================================
// Nuclear Fancy Pants Edition™ -- NUCLEAR+++
// File: /home/ploi/automarketer-ai.com/public/wp-content/plugins/automarketer-ai/includes/class-ga4-auth.php
// Description: GA4 OAuth 2.0 flow, token exchange, encrypted storage, auto-refresh - NUCLEAR+++
// Last Edited: 2026-05-18
// ==================================================

namespace AutoMarketerAI;

defined('ABSPATH') || exit;

final class Ga4Auth
{
    private const AUTH_URL   = 'https://accounts.google.com/o/oauth2/v2/auth';
    private const TOKEN_URL  = 'https://oauth2.googleapis.com/token';
    private const REVOKE_URL = 'https://oauth2.googleapis.com/revoke';

    // GA4 Admin API for listing accounts and properties
    private const ADMIN_API_BASE = 'https://analyticsadmin.googleapis.com/v1beta';

    private const SCOPE = 'https://www.googleapis.com/auth/analytics.readonly';

    // Refresh 5 minutes before expiry
    private const TOKEN_REFRESH_BUFFER_SECONDS = 300;

    public const CRON_HOOK   = 'amai_ga4_token_refresh';
    private const STATE_OPTION = 'amai_ga4_oauth_state';

    // --------------------------------------------------
    // Build the OAuth 2.0 authorization URL
    // --------------------------------------------------
    public static function getAuthUrl(): string
    {
        if (!defined('AMAI_GOOGLE_CLIENT_ID') || (string) AMAI_GOOGLE_CLIENT_ID === '') {
            return '';
        }
        $state = bin2hex(random_bytes(16));
        update_option(self::STATE_OPTION, $state, false);

        $params = [
            'client_id'     => self::getClientId(),
            'redirect_uri'  => self::getCallbackUrl(),
            'response_type' => 'code',
            'scope'         => self::SCOPE,
            'state'         => $state,
            'access_type'   => 'offline',
            'prompt'        => 'consent',
        ];

        return self::AUTH_URL . '?' . http_build_query($params, '', '&', PHP_QUERY_RFC3986);
    }

    // --------------------------------------------------
    // Handle the OAuth callback
    // --------------------------------------------------
    public static function handleCallback(): void
    {
        if (!current_user_can('manage_automarketer_ai')) {
            wp_die('Unauthorized');
        }

        $code  = isset($_GET['code'])  ? sanitize_text_field(wp_unslash($_GET['code']))  : '';
        $state = isset($_GET['state']) ? sanitize_text_field(wp_unslash($_GET['state'])) : '';
        $error = isset($_GET['error']) ? sanitize_text_field(wp_unslash($_GET['error'])) : '';

        if ($error !== '') {
            self::redirectWithError('Google authorization was denied: ' . $error);
            return;
        }

        if ($code === '' || $state === '') {
            self::redirectWithError('Missing authorization code or state parameter.');
            return;
        }

        $savedState = (string) get_option(self::STATE_OPTION, '');
        delete_option(self::STATE_OPTION);

        if ($savedState === '' || !hash_equals($savedState, $state)) {
            self::redirectWithError('Invalid OAuth state. Please try connecting again.');
            return;
        }

        $tokenData = self::exchangeCodeForTokens($code);
        if ($tokenData === null) {
            self::redirectWithError('Failed to exchange authorization code for tokens.');
            return;
        }

        $propertyData = self::fetchFirstProperty($tokenData['access_token']);
        if ($propertyData === null) {
            self::redirectWithError('No GA4 property found. Make sure your Google account has access to at least one GA4 property, then try again.');
            return;
        }

        $saved = self::saveConnection($tokenData, $propertyData);
        if (!$saved) {
            self::redirectWithError('Failed to save GA4 connection.');
            return;
        }

        self::scheduleCron();

        wp_safe_redirect(
            admin_url('admin.php?page=automarketer-ai-modules&amai_ga4_connected=1')
        );
        exit;
    }

    // --------------------------------------------------
    // Disconnect GA4
    // --------------------------------------------------
    public static function disconnect(): bool
    {
        global $wpdb;
        $table = $wpdb->prefix . 'amai_ga4_connection';

        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT access_token_enc FROM `{$table}` WHERE tenant_id = %s LIMIT 1",
                self::getTenantId()
            ),
            ARRAY_A
        );

        if (is_array($row) && !empty($row['access_token_enc'])) {
            $accessToken = KeyVault::decrypt($row['access_token_enc']);
            if (is_string($accessToken) && $accessToken !== '') {
                self::revokeToken($accessToken);
            }
        }

        $wpdb->delete($table, ['tenant_id' => self::getTenantId()]);

        // Clear metrics
        $wpdb->query("DELETE FROM `{$wpdb->prefix}amai_ga4_metrics`");

        self::unscheduleCron();

        return true;
    }

    // --------------------------------------------------
    // Get the active connection row or null
    // --------------------------------------------------
    public static function getConnection(): ?array
    {
        global $wpdb;
        $table = $wpdb->prefix . 'amai_ga4_connection';

        if ($wpdb->get_var("SHOW TABLES LIKE '{$table}'") !== $table) {
            return null;
        }

        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM `{$table}` WHERE tenant_id = %s LIMIT 1",
                self::getTenantId()
            ),
            ARRAY_A
        );

        return is_array($row) ? $row : null;
    }

    // --------------------------------------------------
    // Check if GA4 is connected and active
    // --------------------------------------------------
    public static function isConnected(): bool
    {
        $conn = self::getConnection();
        return $conn !== null && ($conn['status'] ?? '') === 'active';
    }

    // --------------------------------------------------
    // Get a valid access token, refreshing if needed
    // --------------------------------------------------
    public static function getAccessToken(): ?string
    {
        $conn = self::getConnection();
        if ($conn === null || $conn['status'] !== 'active') {
            return null;
        }

        $accessToken = KeyVault::decrypt((string) ($conn['access_token_enc'] ?? ''));
        if (!is_string($accessToken) || $accessToken === '') {
            return null;
        }

        $expiresAt = $conn['token_expires_at'] ?? null;
        if ($expiresAt !== null) {
            $expiresTs = strtotime((string) $expiresAt);
            if ($expiresTs !== false && $expiresTs <= (time() + self::TOKEN_REFRESH_BUFFER_SECONDS)) {
                $refreshed = self::refreshAccessToken();
                if ($refreshed) {
                    $conn = self::getConnection();
                    if ($conn === null) {
                        return null;
                    }
                    $accessToken = KeyVault::decrypt((string) ($conn['access_token_enc'] ?? ''));
                    if (!is_string($accessToken) || $accessToken === '') {
                        return null;
                    }
                }
            }
        }

        return $accessToken;
    }

    // --------------------------------------------------
    // Get the connected property ID (e.g. "properties/123456789")
    // --------------------------------------------------
    public static function getPropertyId(): ?string
    {
        $conn = self::getConnection();
        if ($conn === null) {
            return null;
        }
        $id = (string) ($conn['property_id'] ?? '');
        return $id !== '' ? $id : null;
    }

    // --------------------------------------------------
    // Refresh the access token
    // --------------------------------------------------
    public static function refreshAccessToken(): bool
    {
        global $wpdb;
        $table = $wpdb->prefix . 'amai_ga4_connection';

        $conn = self::getConnection();
        if ($conn === null || $conn['status'] !== 'active') {
            return false;
        }

        $refreshToken = KeyVault::decrypt((string) ($conn['refresh_token_enc'] ?? ''));
        if (!is_string($refreshToken) || $refreshToken === '') {
            return false;
        }

        $response = wp_remote_post(self::TOKEN_URL, [
            'timeout' => 15,
            'headers' => ['Content-Type' => 'application/x-www-form-urlencoded'],
            'body'    => [
                'grant_type'    => 'refresh_token',
                'refresh_token' => $refreshToken,
                'client_id'     => self::getClientId(),
                'client_secret' => self::getClientSecret(),
            ],
        ]);

        if (is_wp_error($response)) {
            return false;
        }

        $httpCode = (int) wp_remote_retrieve_response_code($response);
        $json     = json_decode(wp_remote_retrieve_body($response), true);

        if ($httpCode !== 200 || !is_array($json) || !isset($json['access_token'])) {
            if ($httpCode === 400 || $httpCode === 401) {
                $wpdb->update(
                    $table,
                    ['status' => 'error'],
                    ['tenant_id' => self::getTenantId()]
                );
            }
            return false;
        }

        $expiresIn = isset($json['expires_in']) ? (int) $json['expires_in'] : 3600;

        $wpdb->update(
            $table,
            [
                'access_token_enc' => KeyVault::encrypt((string) $json['access_token']),
                'token_expires_at' => gmdate('Y-m-d H:i:s', time() + $expiresIn),
                'last_verified_at' => gmdate('Y-m-d H:i:s'),
                'status'           => 'active',
            ],
            ['tenant_id' => self::getTenantId()]
        );

        return true;
    }

    // --------------------------------------------------
    // Schedule/unschedule cron
    // --------------------------------------------------
    public static function scheduleCron(): void
    {
        if (!wp_next_scheduled(self::CRON_HOOK)) {
            wp_schedule_event(time(), 'twicedaily', self::CRON_HOOK);
        }
    }

    public static function unscheduleCron(): void
    {
        $ts = wp_next_scheduled(self::CRON_HOOK);
        if ($ts !== false) {
            wp_unschedule_event($ts, self::CRON_HOOK);
        }
    }

    // --------------------------------------------------
    // Exchange authorization code for tokens
    // --------------------------------------------------
    private static function exchangeCodeForTokens(string $code): ?array
    {
        $response = wp_remote_post(self::TOKEN_URL, [
            'timeout' => 15,
            'headers' => ['Content-Type' => 'application/x-www-form-urlencoded'],
            'body'    => [
                'grant_type'    => 'authorization_code',
                'code'          => $code,
                'redirect_uri'  => self::getCallbackUrl(),
                'client_id'     => self::getClientId(),
                'client_secret' => self::getClientSecret(),
            ],
        ]);

        if (is_wp_error($response)) {
            return null;
        }

        $httpCode = (int) wp_remote_retrieve_response_code($response);
        $json     = json_decode(wp_remote_retrieve_body($response), true);

        if ($httpCode !== 200 || !is_array($json) || !isset($json['access_token'])) {
            return null;
        }

        return [
            'access_token'  => (string) $json['access_token'],
            'refresh_token' => isset($json['refresh_token']) ? (string) $json['refresh_token'] : '',
            'expires_in'    => isset($json['expires_in']) ? (int) $json['expires_in'] : 3600,
        ];
    }

    // --------------------------------------------------
    // Fetch the first accessible GA4 property
    // --------------------------------------------------
    private static function fetchFirstProperty(string $accessToken): ?array
    {
        // Step 1 -- list GA4 accounts
        $accountsResp = wp_remote_get(self::ADMIN_API_BASE . '/accounts', [
            'timeout' => 15,
            'headers' => ['Authorization' => 'Bearer ' . $accessToken],
        ]);

        if (is_wp_error($accountsResp)) {
            return null;
        }

        $accountsJson = json_decode(wp_remote_retrieve_body($accountsResp), true);
        if (!is_array($accountsJson) || empty($accountsJson['accounts'])) {
            return null;
        }

        $accountName = (string) ($accountsJson['accounts'][0]['name'] ?? '');
        if ($accountName === '') {
            return null;
        }

        $googleAccountId = basename($accountName);

        // Step 2 -- list properties under the first account
        $propsResp = wp_remote_get(
            self::ADMIN_API_BASE . '/properties?filter=ancestor:' . rawurlencode($accountName) . '&pageSize=10',
            [
                'timeout' => 15,
                'headers' => ['Authorization' => 'Bearer ' . $accessToken],
            ]
        );

        if (is_wp_error($propsResp)) {
            return null;
        }

        $propsJson = json_decode(wp_remote_retrieve_body($propsResp), true);
        if (!is_array($propsJson) || empty($propsJson['properties'])) {
            return null;
        }

        $prop         = $propsJson['properties'][0];
        $propertyName = (string) ($prop['name'] ?? '');
        $displayName  = (string) ($prop['displayName'] ?? '');

        if ($propertyName === '') {
            return null;
        }

        // property_id stored as full resource name e.g. "properties/123456789"
        return [
            'google_account_id' => $googleAccountId,
            'property_id'       => $propertyName,
            'property_name'     => $displayName !== '' ? $displayName : $propertyName,
        ];
    }

    // --------------------------------------------------
    // Save or update the connection row
    // --------------------------------------------------
    private static function saveConnection(array $tokenData, array $propertyData): bool
    {
        global $wpdb;
        $table    = $wpdb->prefix . 'amai_ga4_connection';
        $tenantId = self::getTenantId();

        $refreshToken = $tokenData['refresh_token'];
        if ($refreshToken === '') {
            $existing = self::getConnection();
            if ($existing !== null && !empty($existing['refresh_token_enc'])) {
                $refreshToken = KeyVault::decrypt($existing['refresh_token_enc']);
            }
        }

        $data = [
            'google_account_id' => $propertyData['google_account_id'],
            'property_id'       => $propertyData['property_id'],
            'property_name'     => $propertyData['property_name'],
            'access_token_enc'  => KeyVault::encrypt($tokenData['access_token']),
            'refresh_token_enc' => $refreshToken !== '' ? KeyVault::encrypt($refreshToken) : '',
            'token_expires_at'  => gmdate('Y-m-d H:i:s', time() + $tokenData['expires_in']),
            'connected_at'      => gmdate('Y-m-d H:i:s'),
            'last_verified_at'  => gmdate('Y-m-d H:i:s'),
            'status'            => 'active',
        ];

        $existingRow = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT id FROM `{$table}` WHERE tenant_id = %s LIMIT 1",
                $tenantId
            )
        );

        if ($existingRow !== null) {
            $wpdb->update($table, $data, ['tenant_id' => $tenantId]);
        } else {
            $data['tenant_id'] = $tenantId;
            $wpdb->insert($table, $data);
        }

        return $wpdb->last_error === '';
    }

    // --------------------------------------------------
    // Revoke an access token
    // --------------------------------------------------
    private static function revokeToken(string $token): void
    {
        wp_remote_post(self::REVOKE_URL . '?token=' . rawurlencode($token), [
            'timeout' => 10,
            'headers' => ['Content-Type' => 'application/x-www-form-urlencoded'],
        ]);
    }

    // --------------------------------------------------
    // Redirect with error to modules page
    // --------------------------------------------------
    private static function redirectWithError(string $message): void
    {
        wp_safe_redirect(
            admin_url('admin.php?page=automarketer-ai-modules&amai_ga4_error=' . rawurlencode($message))
        );
        exit;
    }

    // --------------------------------------------------
    // Callback URL
    // --------------------------------------------------
    private static function getCallbackUrl(): string
    {
        return home_url('/ga4/callback');
    }

    // --------------------------------------------------
    // Client credentials -- reuse existing Google OAuth app constants
    // --------------------------------------------------
    private static function getClientId(): string
    {
        if (!defined('AMAI_GOOGLE_CLIENT_ID') || AMAI_GOOGLE_CLIENT_ID === '') {
            return '';
        }
        return AMAI_GOOGLE_CLIENT_ID;
    }

    private static function getClientSecret(): string
    {
        if (!defined('AMAI_GOOGLE_CLIENT_SECRET') || AMAI_GOOGLE_CLIENT_SECRET === '') {
            return '';
        }
        return AMAI_GOOGLE_CLIENT_SECRET;
    }

    // --------------------------------------------------
    // Tenant ID
    // --------------------------------------------------
    private static function getTenantId(): string
    {
        return (string) get_option('amai_tenant_id', 'default');
    }
}