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

namespace AutoMarketerAI;

defined('ABSPATH') || exit;

final class YelpAuth
{
    // Yelp API endpoints
    private const AUTH_BASE   = 'https://www.yelp.com/oauth2/authorization';
    private const TOKEN_URL   = 'https://api.yelp.com/oauth2/token';
    private const BUSINESS_URL = 'https://api.yelp.com/v3/businesses/';

    // Scopes
    private const SCOPES = ['businesses'];

    // wp_options keys
    private const STATE_OPTION = 'amai_yelp_oauth_state';

    // WP cron hook (Yelp tokens are long-lived but we verify periodically)
    public const CRON_HOOK = 'amai_yelp_token_refresh';

    // Access token lifetime -- Yelp tokens do not expire by default
    // We set a generous TTL for record-keeping purposes only
    private const ACCESS_TOKEN_TTL = 31536000; // 365 days

    // Avatar attachment option key
    private const AVATAR_ATTACHMENT_OPTION = 'amai_yelp_avatar_attachment_id';

    // --------------------------------------------------
    // Public: build the OAuth authorization URL
    // --------------------------------------------------
    public static function getAuthUrl(): string
    {
        if (self::getClientId() === '' || self::getClientSecret() === '') {
            return '';
        }

        $state = bin2hex(random_bytes(16));
        update_option(self::STATE_OPTION, $state, false);

        $params = [
            'response_type' => 'code',
            'client_id'     => self::getClientId(),
            'redirect_uri'  => self::getCallbackUrl(),
            'scope'         => implode(',', self::SCOPES),
            'state'         => $state,
        ];

        return self::AUTH_BASE . '?' . http_build_query($params);
    }

    // --------------------------------------------------
    // Public: callback URL for Yelp OAuth redirect
    // --------------------------------------------------
    public static function getCallbackUrl(): string
    {
        return home_url('/yelp/callback');
    }

    // --------------------------------------------------
    // Public: handle the OAuth callback
    // --------------------------------------------------
    public static function handleCallback(): void
    {
        $code  = sanitize_text_field((string) ($_GET['code'] ?? ''));
        $state = sanitize_text_field((string) ($_GET['state'] ?? ''));
        $error = sanitize_text_field((string) ($_GET['error'] ?? ''));

        if ($error !== '') {
            self::redirectWithError('yelp_auth_denied');
            return;
        }

        if ($code === '' || $state === '') {
            self::redirectWithError('yelp_missing_params');
            return;
        }

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

        if (!hash_equals($stored_state, $state)) {
            self::redirectWithError('yelp_invalid_state');
            return;
        }

        $token_data = self::exchangeCode($code);

        if (isset($token_data['error'])) {
            self::redirectWithError('yelp_token_exchange_failed');
            return;
        }

        $access_token = (string) ($token_data['access_token'] ?? '');

        if ($access_token === '') {
            self::redirectWithError('yelp_empty_token');
            return;
        }

        // Yelp requires the user to tell us which business they are.
        // Store the token first, then redirect to a business picker step.
        self::saveTokenPending($access_token);

        wp_safe_redirect(
            admin_url('admin.php?page=automarketer-ai-social&channel=yelp&pick_business=1')
        );
        exit;
    }

    // --------------------------------------------------
    // Public: save the selected business after OAuth
    // Called via AJAX when the user picks their business
    // --------------------------------------------------
    public static function saveBusiness(string $business_id, string $access_token): bool
    {
        $info = self::fetchBusinessInfo($business_id, $access_token);

        if (isset($info['error'])) {
            return false;
        }

        $business_name = (string) ($info['name'] ?? '');
        $business_url  = (string) ($info['url'] ?? '');
        $avatar_url    = (string) ($info['image_url'] ?? '');
        $review_count  = (int) ($info['review_count'] ?? 0);
        $rating        = (float) ($info['rating'] ?? 0.0);

        self::saveConnection(
            $business_id,
            $business_name,
            $business_url,
            $avatar_url,
            $review_count,
            $rating,
            $access_token
        );

        self::scheduleCron();

        return true;
    }

    // --------------------------------------------------
    // Public: return the active connection row or null
    // --------------------------------------------------
    public static function getConnection(): ?array
    {
        global $wpdb;

        $table = $wpdb->prefix . 'amai_yelp_connection';
        $row   = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM `{$table}` WHERE tenant_id = %s AND status = 'active' AND yelp_business_id != '' LIMIT 1",
                'default'
            ),
            ARRAY_A
        );

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

    // --------------------------------------------------
    // Public: return true if fully connected (business selected)
    // --------------------------------------------------
    public static function isConnected(): bool
    {
        return self::getConnection() !== null;
    }

    // --------------------------------------------------
    // Public: return the decrypted access token or null
    // --------------------------------------------------
    public static function getAccessToken(): ?string
    {
        global $wpdb;

        // Check both pending and active rows
        $table = $wpdb->prefix . 'amai_yelp_connection';
        $row   = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT access_token_enc FROM `{$table}` WHERE tenant_id = %s LIMIT 1",
                'default'
            ),
            ARRAY_A
        );

        if (!is_array($row)) {
            return null;
        }

        $encrypted = (string) ($row['access_token_enc'] ?? '');
        if ($encrypted === '') {
            return null;
        }

        $decrypted = KeyVault::decrypt($encrypted);

        return ($decrypted !== '' && $decrypted !== false) ? $decrypted : null;
    }

    // --------------------------------------------------
    // Public: return true if token exists but business not yet selected
    // --------------------------------------------------
    public static function isPendingBusinessSelection(): bool
    {
        global $wpdb;

        $table = $wpdb->prefix . 'amai_yelp_connection';
        $row   = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT yelp_business_id FROM `{$table}` WHERE tenant_id = %s AND status = 'active' LIMIT 1",
                'default'
            ),
            ARRAY_A
        );

        if (!is_array($row)) {
            return false;
        }

        return (string) ($row['yelp_business_id'] ?? '') === '';
    }

    // --------------------------------------------------
    // Public: search for businesses by name/location
    // --------------------------------------------------
    public static function searchBusinesses(string $term, string $location): array
    {
        $token = self::getAccessToken();
        if ($token === null) {
            return ['error' => 'no_access_token'];
        }

        $response = wp_remote_get(
            'https://api.yelp.com/v3/businesses/search?' . http_build_query([
                'term'     => $term,
                'location' => $location,
                'limit'    => 10,
            ]),
            [
                'timeout'   => 15,
                'sslverify' => true,
                'headers'   => [
                    'Authorization' => 'Bearer ' . $token,
                ],
            ]
        );

        return self::parseApiResponse($response);
    }

    // --------------------------------------------------
    // Public: disconnect
    // --------------------------------------------------
    public static function disconnect(): bool
    {
        global $wpdb;

        self::deleteAvatarAttachment();
        self::unscheduleCron();

        $table  = $wpdb->prefix . 'amai_yelp_connection';
        $result = $wpdb->update(
            $table,
            ['status' => 'revoked'],
            ['tenant_id' => 'default']
        );

        return $result !== false;
    }

    // --------------------------------------------------
    // Public: verify stored token is still valid
    // --------------------------------------------------
    public static function maybeRefreshToken(): bool
    {
        // Yelp tokens do not expire -- just verify the connection is live
        $connection = self::getConnection();
        if ($connection === null) {
            return true;
        }

        $token = self::getAccessToken();
        if ($token === null) {
            return false;
        }

        $business_id = (string) ($connection['yelp_business_id'] ?? '');
        if ($business_id === '') {
            return false;
        }

        global $wpdb;

        $info = self::fetchBusinessInfo($business_id, $token);
        if (isset($info['error'])) {
            $wpdb->update(
                $wpdb->prefix . 'amai_yelp_connection',
                ['status' => 'expired'],
                ['tenant_id' => 'default']
            );
            return false;
        }

        $wpdb->update(
            $wpdb->prefix . 'amai_yelp_connection',
            [
                'review_count'     => (int) ($info['review_count'] ?? 0),
                'rating'           => (float) ($info['rating'] ?? 0.0),
                'last_verified_at' => current_time('mysql', true),
            ],
            ['tenant_id' => 'default']
        );

        return true;
    }

    // --------------------------------------------------
    // Public: schedule weekly verification cron
    // --------------------------------------------------
    public static function scheduleCron(): void
    {
        if (!wp_next_scheduled(self::CRON_HOOK)) {
            wp_schedule_event(time(), 'weekly', self::CRON_HOOK);
        }
    }

    // --------------------------------------------------
    // Public: unschedule cron
    // --------------------------------------------------
    public static function unscheduleCron(): void
    {
        $timestamp = wp_next_scheduled(self::CRON_HOOK);
        if ($timestamp !== false) {
            wp_unschedule_event($timestamp, self::CRON_HOOK);
        }
    }

    // --------------------------------------------------
    // Private: exchange auth code for access token
    // --------------------------------------------------
    private static function exchangeCode(string $code): array
    {
        $response = wp_remote_post(
            self::TOKEN_URL,
            [
                'timeout'   => 15,
                'sslverify' => true,
                'headers'   => [
                    'Content-Type' => 'application/x-www-form-urlencoded',
                ],
                'body' => http_build_query([
                    'grant_type'    => 'authorization_code',
                    'client_id'     => self::getClientId(),
                    'client_secret' => self::getClientSecret(),
                    'redirect_uri'  => self::getCallbackUrl(),
                    'code'          => $code,
                ]),
            ]
        );

        return self::parseApiResponse($response);
    }

    // --------------------------------------------------
    // Private: fetch business info from Yelp API
    // --------------------------------------------------
    private static function fetchBusinessInfo(string $business_id, string $token): array
    {
        $response = wp_remote_get(
            self::BUSINESS_URL . rawurlencode($business_id),
            [
                'timeout'   => 15,
                'sslverify' => true,
                'headers'   => [
                    'Authorization' => 'Bearer ' . $token,
                ],
            ]
        );

        return self::parseApiResponse($response);
    }

    // --------------------------------------------------
    // Private: save a pending token row (business not yet selected)
    // --------------------------------------------------
    private static function saveTokenPending(string $access_token): void
    {
        global $wpdb;

        $table       = $wpdb->prefix . 'amai_yelp_connection';
        $access_enc  = KeyVault::encrypt($access_token);
        $expires_at  = gmdate('Y-m-d H:i:s', time() + self::ACCESS_TOKEN_TTL);
        $now         = current_time('mysql', true);

        $exists = (int) $wpdb->get_var(
            $wpdb->prepare(
                "SELECT COUNT(*) FROM `{$table}` WHERE tenant_id = %s",
                'default'
            )
        );

        if ($exists > 0) {
            $wpdb->update(
                $table,
                [
                    'access_token_enc' => $access_enc,
                    'token_expires_at' => $expires_at,
                    'connected_at'     => $now,
                    'last_verified_at' => $now,
                    'yelp_business_id' => '',
                    'status'           => 'active',
                ],
                ['tenant_id' => 'default']
            );
        } else {
            $wpdb->insert(
                $table,
                [
                    'tenant_id'        => 'default',
                    'yelp_business_id' => '',
                    'business_name'    => '',
                    'business_url'     => '',
                    'review_count'     => 0,
                    'rating'           => 0.0,
                    'access_token_enc' => $access_enc,
                    'token_expires_at' => $expires_at,
                    'connected_at'     => $now,
                    'last_verified_at' => $now,
                    'status'           => 'active',
                ]
            );
        }
    }

    // --------------------------------------------------
    // Private: persist the full connection to the database
    // Uses raw SQL because URLs may contain %
    // --------------------------------------------------
    private static function saveConnection(
        string $business_id,
        string $business_name,
        string $business_url,
        string $avatar_url,
        int    $review_count,
        float  $rating,
        string $access_token
    ): void {
        global $wpdb;

        $table      = $wpdb->prefix . 'amai_yelp_connection';
        $access_enc = KeyVault::encrypt($access_token);

        $local_avatar  = self::downloadAvatar($avatar_url);
        $stored_avatar = ($local_avatar !== null) ? $local_avatar : null;

        $expires_at = gmdate('Y-m-d H:i:s', time() + self::ACCESS_TOKEN_TTL);
        $now        = current_time('mysql', true);

        $exists = (int) $wpdb->get_var(
            $wpdb->prepare(
                "SELECT COUNT(*) FROM `{$table}` WHERE tenant_id = %s",
                'default'
            )
        );

        if ($exists > 0) {
            $wpdb->query(
                $wpdb->prepare(
                    "UPDATE `{$table}` SET
                        yelp_business_id = %s, business_name = %s, business_url = %s,
                        avatar_url = %s, review_count = %d, rating = %f,
                        access_token_enc = %s, token_expires_at = %s,
                        connected_at = %s, last_verified_at = %s,
                        status = 'active'
                    WHERE tenant_id = %s",
                    $business_id,
                    $business_name,
                    $business_url,
                    $stored_avatar,
                    $review_count,
                    $rating,
                    $access_enc,
                    $expires_at,
                    $now,
                    $now,
                    'default'
                )
            );
        } else {
            $wpdb->query(
                $wpdb->prepare(
                    "INSERT INTO `{$table}` (
                        tenant_id, yelp_business_id, business_name, business_url,
                        avatar_url, review_count, rating,
                        access_token_enc, token_expires_at,
                        connected_at, last_verified_at, status
                    ) VALUES (%s, %s, %s, %s, %s, %d, %f, %s, %s, %s, %s, 'active')",
                    'default',
                    $business_id,
                    $business_name,
                    $business_url,
                    $stored_avatar,
                    $review_count,
                    $rating,
                    $access_enc,
                    $expires_at,
                    $now,
                    $now
                )
            );
        }
    }

    // --------------------------------------------------
    // Private: download business image to WP media library
    // --------------------------------------------------
    private static function downloadAvatar(string $url): ?string
    {
        if ($url === '') {
            return null;
        }

        self::deleteAvatarAttachment();

        require_once ABSPATH . 'wp-admin/includes/media.php';
        require_once ABSPATH . 'wp-admin/includes/file.php';
        require_once ABSPATH . 'wp-admin/includes/image.php';

        $tmp = download_url($url);
        if (is_wp_error($tmp)) {
            return null;
        }

        $file_array = [
            'name'     => 'yelp-business-' . time() . '.jpg',
            'tmp_name' => $tmp,
        ];

        $attachment_id = media_handle_sideload($file_array, 0, 'Yelp business image');

        if (is_wp_error($attachment_id)) {
            if (is_file($tmp)) {
                wp_delete_file($tmp);
            }
            return null;
        }

        update_option(self::AVATAR_ATTACHMENT_OPTION, $attachment_id, false);

        return (string) wp_get_attachment_url($attachment_id);
    }

    // --------------------------------------------------
    // Private: delete stored avatar attachment
    // --------------------------------------------------
    private static function deleteAvatarAttachment(): void
    {
        $attachment_id = (int) get_option(self::AVATAR_ATTACHMENT_OPTION, 0);
        if ($attachment_id <= 0) {
            return;
        }

        wp_delete_attachment($attachment_id, true);
        delete_option(self::AVATAR_ATTACHMENT_OPTION);
    }

    // --------------------------------------------------
    // Private: parse a Yelp API response
    // --------------------------------------------------
    private static function parseApiResponse(mixed $response): array
    {
        if (is_wp_error($response)) {
            return ['error' => $response->get_error_message()];
        }

        $code = (int) wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        $data = json_decode($body, true);

        if (!is_array($data)) {
            return ['error' => 'invalid_json_response'];
        }

        if (isset($data['error']['code'])) {
            return ['error' => (string) $data['error']['code']];
        }

        if ($code < 200 || $code >= 300) {
            return ['error' => 'http_' . $code];
        }

        return $data;
    }

    // --------------------------------------------------
    // Private: redirect to social page with error
    // --------------------------------------------------
    private static function redirectWithError(string $error_code): void
    {
        set_transient('amai_yelp_auth_error', $error_code, 60);
        wp_safe_redirect(
            admin_url('admin.php?page=automarketer-ai-social&channel=yelp&auth_error=1')
        );
        exit;
    }

    // --------------------------------------------------
    // Private: get Yelp Client ID from wp-config.php
    // --------------------------------------------------
    private static function getClientId(): string
    {
        return defined('AMAI_YELP_CLIENT_ID') ? (string) AMAI_YELP_CLIENT_ID : '';
    }

    // --------------------------------------------------
    // Private: get Yelp Client Secret from wp-config.php
    // --------------------------------------------------
    private static function getClientSecret(): string
    {
        return defined('AMAI_YELP_CLIENT_SECRET') ? (string) AMAI_YELP_CLIENT_SECRET : '';
    }
}