<?php
defined( 'ABSPATH' ) || exit;

/**
 * Pipeline Buster — Google Search Console Integration
 *
 * Handles OAuth 2.0 authorization, token management, baseline snapshots,
 * and current-vs-baseline metric comparisons.
 *
 * Token exchange (authorization_code + refresh_token grants) is routed
 * through a pipelinebuster.com proxy that holds the Google OAuth client
 * secret server-side — distributed plugin code never contains it.
 */
class PIPEBU_GSC {

    private const CLIENT_ID  = '22156554606-c8ej9nt44avhuhugg2i20h6htqad3kt1.apps.googleusercontent.com';
    private const SCOPE      = 'https://www.googleapis.com/auth/webmasters.readonly';
    private const OPTION_KEY = 'pipebu_gsc_data';
    private const PROXY_URL  = 'https://pipelinebuster.com/api/gsc-proxy.php';

    // -------------------------------------------------------------------------
    // OAuth flow
    // -------------------------------------------------------------------------

    public static function redirect_uri(): string {
        return admin_url( 'admin-ajax.php' ) . '?action=pb_gsc_callback';
    }

    public static function get_auth_url(): string {
        $state = wp_create_nonce( 'pipebu_gsc_state' );

        return 'https://accounts.google.com/o/oauth2/v2/auth?' . http_build_query( [
            'client_id'     => self::CLIENT_ID,
            'redirect_uri'  => self::redirect_uri(),
            'response_type' => 'code',
            'scope'         => self::SCOPE,
            'access_type'   => 'offline',
            'prompt'        => 'consent',
            'state'         => $state,
        ] );
    }

    /**
     * Exchange authorization code for tokens. Called from the OAuth callback.
     * Returns true on success, false on failure.
     */
    public static function handle_callback( string $code ): bool {
        $response = wp_remote_post( self::PROXY_URL, [
            'body' => [
                'grant_type'   => 'authorization_code',
                'code'         => sanitize_text_field( $code ),
                'redirect_uri' => self::redirect_uri(),
            ],
            'timeout' => 15,
        ] );

        if ( is_wp_error( $response ) ) return false;

        $data = json_decode( wp_remote_retrieve_body( $response ), true );
        if ( empty( $data['access_token'] ) ) return false;

        $stored = self::get_data();
        $stored['access_token']  = self::encrypt( $data['access_token'] );
        $stored['token_expires'] = time() + ( (int) $data['expires_in'] - 60 );
        if ( ! empty( $data['refresh_token'] ) ) {
            $stored['refresh_token'] = self::encrypt( $data['refresh_token'] );
        }
        self::save_data( $stored );
        return true;
    }

    // -------------------------------------------------------------------------
    // Connection status
    // -------------------------------------------------------------------------

    public static function is_connected(): bool {
        $data = self::get_data();
        return ! empty( $data['access_token'] );
    }

    public static function disconnect(): void {
        delete_option( self::OPTION_KEY );
    }

    // -------------------------------------------------------------------------
    // Site management
    // -------------------------------------------------------------------------

    /**
     * Returns array of site entries from GSC, each with 'siteUrl' and 'permissionLevel'.
     */
    public static function get_sites(): array {
        $token = self::get_access_token();
        if ( ! $token ) return [];

        $response = wp_remote_get( 'https://www.googleapis.com/webmasters/v3/sites', [
            'headers' => [ 'Authorization' => 'Bearer ' . $token ],
            'timeout' => 15,
        ] );

        if ( is_wp_error( $response ) ) return [];
        $data = json_decode( wp_remote_retrieve_body( $response ), true );
        return $data['siteEntry'] ?? [];
    }

    public static function get_selected_site(): string {
        return self::get_data()['site_url'] ?? '';
    }

    public static function set_selected_site( string $url ): void {
        $data = self::get_data();
        $data['site_url'] = esc_url_raw( $url );
        self::save_data( $data );
    }

    // -------------------------------------------------------------------------
    // Baseline + metrics
    // -------------------------------------------------------------------------

    /**
     * Captures a T=0 snapshot of the last 90 days from GSC.
     * Stored in options. Returns the snapshot array or false on failure.
     */
    public static function take_baseline(): array|false {
        $site_url = self::get_selected_site();
        $token    = self::get_access_token();
        if ( ! $site_url || ! $token ) return false;

        $end   = gmdate( 'Y-m-d', strtotime( '-1 day' ) );
        $start = gmdate( 'Y-m-d', strtotime( '-90 days' ) );

        // Aggregate totals (no dimension breakdown).
        $agg = self::search_analytics_query( $token, $site_url, $start, $end, [], 1 );
        if ( $agg === false ) return false;

        $row         = $agg['rows'][0] ?? [];
        $impressions = (int) ( $row['impressions'] ?? 0 );
        $clicks      = (int) ( $row['clicks']      ?? 0 );
        $position    = round( (float) ( $row['position'] ?? 0 ), 1 );

        // Keyword count: pull query dimension, count unique rows.
        $kw_data  = self::search_analytics_query( $token, $site_url, $start, $end, [ 'query' ], 1000 );
        $keywords = count( $kw_data['rows'] ?? [] );

        $baseline = [
            'date'        => gmdate( 'Y-m-d' ),
            'period'      => "$start to $end",
            'impressions' => $impressions,
            'clicks'      => $clicks,
            'position'    => $position,
            'keywords'    => $keywords,
        ];

        $data = self::get_data();
        $data['baseline'] = $baseline;
        self::save_data( $data );

        return $baseline;
    }

    public static function get_baseline(): array {
        return self::get_data()['baseline'] ?? [];
    }

    /**
     * Pulls current 90-day metrics (same window as baseline for comparison).
     */
    public static function get_current_metrics(): array|false {
        $site_url = self::get_selected_site();
        $token    = self::get_access_token();
        if ( ! $site_url || ! $token ) return false;

        $end   = gmdate( 'Y-m-d', strtotime( '-1 day' ) );
        $start = gmdate( 'Y-m-d', strtotime( '-90 days' ) );

        $agg = self::search_analytics_query( $token, $site_url, $start, $end, [], 1 );
        if ( $agg === false ) return false;

        $row         = $agg['rows'][0] ?? [];
        $impressions = (int) ( $row['impressions'] ?? 0 );
        $clicks      = (int) ( $row['clicks']      ?? 0 );
        $position    = round( (float) ( $row['position'] ?? 0 ), 1 );

        $kw_data  = self::search_analytics_query( $token, $site_url, $start, $end, [ 'query' ], 1000 );
        $keywords = count( $kw_data['rows'] ?? [] );

        return compact( 'impressions', 'clicks', 'position', 'keywords' );
    }

    // -------------------------------------------------------------------------
    // Token management
    // -------------------------------------------------------------------------

    private static function get_access_token(): string {
        $data = self::get_data();
        if ( empty( $data['access_token'] ) ) return '';

        // Auto-refresh if expired.
        if ( ! empty( $data['token_expires'] ) && time() > (int) $data['token_expires'] ) {
            if ( empty( $data['refresh_token'] ) ) return '';
            $refreshed = self::refresh_token( self::decrypt( $data['refresh_token'] ) );
            if ( ! $refreshed ) return '';
            $data['access_token']  = self::encrypt( $refreshed['access_token'] );
            $data['token_expires'] = time() + ( (int) $refreshed['expires_in'] - 60 );
            self::save_data( $data );
            return $refreshed['access_token'];
        }

        return self::decrypt( $data['access_token'] );
    }

    private static function refresh_token( string $refresh_token ): array|false {
        $response = wp_remote_post( self::PROXY_URL, [
            'body' => [
                'grant_type'    => 'refresh_token',
                'refresh_token' => $refresh_token,
            ],
            'timeout' => 15,
        ] );

        if ( is_wp_error( $response ) ) return false;
        $data = json_decode( wp_remote_retrieve_body( $response ), true );
        return ! empty( $data['access_token'] ) ? $data : false;
    }

    // -------------------------------------------------------------------------
    // GSC API
    // -------------------------------------------------------------------------

    private static function search_analytics_query(
        string $token,
        string $site_url,
        string $start,
        string $end,
        array  $dimensions = [],
        int    $row_limit   = 1
    ): array|false {
        $encoded = rawurlencode( $site_url );
        $url     = "https://www.googleapis.com/webmasters/v3/sites/{$encoded}/searchAnalytics/query";

        $body = [
            'startDate' => $start,
            'endDate'   => $end,
            'rowLimit'  => $row_limit,
        ];
        if ( $dimensions ) {
            $body['dimensions'] = $dimensions;
        }

        $response = wp_remote_post( $url, [
            'headers' => [
                'Authorization' => 'Bearer ' . $token,
                'Content-Type'  => 'application/json',
            ],
            'body'    => wp_json_encode( $body ),
            'timeout' => 20,
        ] );

        if ( is_wp_error( $response ) ) return false;
        $data = json_decode( wp_remote_retrieve_body( $response ), true );
        return is_array( $data ) ? $data : false;
    }

    // -------------------------------------------------------------------------
    // Storage
    // -------------------------------------------------------------------------

    private static function get_data(): array {
        return get_option( self::OPTION_KEY, [] );
    }

    private static function save_data( array $data ): void {
        update_option( self::OPTION_KEY, $data );
    }

    // -------------------------------------------------------------------------
    // Encryption (same AES-256-CBC pattern as PIPEBU_Settings)
    // -------------------------------------------------------------------------

    private static function encrypt( string $value ): string {
        if ( ! $value ) return '';
        $key    = substr( hash( 'sha256', AUTH_KEY . SECURE_AUTH_KEY ), 0, 32 );
        $iv     = random_bytes( 16 );
        $cipher = openssl_encrypt( $value, 'aes-256-cbc', $key, 0, $iv );
        return base64_encode( $iv . $cipher );
    }

    private static function decrypt( string $value ): string {
        if ( ! $value ) return '';
        $key    = substr( hash( 'sha256', AUTH_KEY . SECURE_AUTH_KEY ), 0, 32 );
        $raw    = base64_decode( $value );
        $iv     = substr( $raw, 0, 16 );
        $cipher = substr( $raw, 16 );
        $plain  = openssl_decrypt( $cipher, 'aes-256-cbc', $key, 0, $iv );
        return $plain ?: '';
    }
}
