<?php
/**
 * PayPal Subscriptions API Client for BoostBot.
 *
 * @package BoostBot
 * @since 1.0.0
 */

// Prevent direct access to the file.
if (!defined('ABSPATH')) {
    exit;
}

/**
 * BoostBot_PayPal_API class.
 *
 * Handles PayPal Subscriptions API integration.
 *
 * @since 1.0.0
 */
class BoostBot_PayPal_API
{
    /**
     * PayPal Sandbox API URL.
     *
     * @since 1.0.0
     * @var string
     */
    const SANDBOX_URL = 'https://api-m.sandbox.paypal.com';

    /**
     * PayPal Live API URL.
     *
     * @since 1.0.0
     * @var string
     */
    const LIVE_URL = 'https://api-m.paypal.com';

    /**
     * Settings option name.
     *
     * @since 1.0.0
     * @var string
     */
    const SETTINGS_OPTION = 'boostbot_paypal_settings';

    /**
     * Client ID.
     *
     * @since 1.0.0
     * @var string
     */
    private $client_id;

    /**
     * Client Secret.
     *
     * @since 1.0.0
     * @var string
     */
    private $client_secret;

    /**
     * Sandbox mode flag.
     *
     * @since 1.0.0
     * @var bool
     */
    private $sandbox_mode;

    /**
     * API base URL.
     *
     * @since 1.0.0
     * @var string
     */
    private $api_base;

    /**
     * Constructor.
     *
     * Priority for credentials:
     * 1. Hardcoded constants (BOOSTBOT_PAYPAL_CLIENT_ID, BOOSTBOT_PAYPAL_SECRET) - for distributed plugin
     * 2. credentials.env file (for development only)
     * 3. Database settings (for backward compatibility)
     *
     * In production, all customers use Boost Media's PayPal credentials.
     *
     * @since 1.0.0
     */
    public function __construct()
    {
        $settings = get_option(self::SETTINGS_OPTION, []);

        // Priority 1: Hardcoded constants (for distributed plugin)
        if (defined('BOOSTBOT_PAYPAL_CLIENT_ID') && !empty(BOOSTBOT_PAYPAL_CLIENT_ID)) {
            $this->client_id = BOOSTBOT_PAYPAL_CLIENT_ID;
        } else {
            // Priority 2: credentials.env (for development)
            $env_credentials = $this->load_from_credentials_env();
            if (!empty($env_credentials['client_id'])) {
                $this->client_id = $env_credentials['client_id'];
            } else {
                // Priority 3: Database settings (backward compatibility)
                $this->client_id = $settings['client_id'] ?? '';
            }
        }
        
        // Same priority for secret
        if (defined('BOOSTBOT_PAYPAL_SECRET') && !empty(BOOSTBOT_PAYPAL_SECRET)) {
            $this->client_secret = BOOSTBOT_PAYPAL_SECRET;
        } else {
            $env_credentials = $env_credentials ?? $this->load_from_credentials_env();
            if (!empty($env_credentials['client_secret'])) {
                $this->client_secret = $env_credentials['client_secret'];
            } else {
                $this->client_secret = $settings['client_secret'] ?? '';
            }
        }

        // Mode: constant > settings > default (live)
        if (defined('BOOSTBOT_PAYPAL_MODE')) {
            $this->sandbox_mode = (BOOSTBOT_PAYPAL_MODE === 'sandbox');
        } else {
            $this->sandbox_mode = isset($settings['sandbox_mode']) ? (bool) $settings['sandbox_mode'] : false;
        }

        $this->api_base = $this->sandbox_mode ? self::SANDBOX_URL : self::LIVE_URL;
    }

    /**
     * Load PayPal credentials from credentials.env file.
     *
     * @since 1.0.0
     * @return array Array with 'client_id' and 'client_secret' keys, or empty array if not found.
     */
    private function load_from_credentials_env(): array
    {
        $credentials = [];
        
        // Try to find credentials.env in plugin root or WordPress root
        $possible_paths = [
            BOOSTBOT_PLUGIN_DIR . '../credentials.env', // Project root
            ABSPATH . 'credentials.env', // WordPress root
            BOOSTBOT_PLUGIN_DIR . 'credentials.env', // Plugin directory
        ];

        $env_file = null;
        foreach ($possible_paths as $path) {
            if (file_exists($path)) {
                $env_file = $path;
                break;
            }
        }

        if (!$env_file) {
            return $credentials;
        }

        // Parse .env file (simple format: KEY=VALUE)
        $lines = file($env_file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
        if (!$lines) {
            return $credentials;
        }

        foreach ($lines as $line) {
            // Skip comments
            if (strpos(trim($line), '#') === 0) {
                continue;
            }

            // Parse KEY=VALUE
            if (strpos($line, '=') !== false) {
                list($key, $value) = explode('=', $line, 2);
                $key = trim($key);
                $value = trim($value);

                // Remove quotes if present
                $value = trim($value, '"\'');
                
                // Map to our internal keys (support multiple formats)
                if ($key === 'Paypal_Client_ID' || $key === 'PAYPAL_CLIENT_ID' || $key === 'Client ID') {
                    $credentials['client_id'] = $value;
                } elseif ($key === 'Paypal_Secret_Key' || $key === 'PAYPAL_SECRET_KEY' || $key === 'Secret key 1') {
                    $credentials['client_secret'] = $value;
                }
            }
        }

        return $credentials;
    }

    /**
     * Check if PayPal is configured.
     *
     * @since 1.0.0
     * @return bool True if configured.
     */
    public function is_configured(): bool
    {
        return !empty($this->client_id) && !empty($this->client_secret);
    }

    /**
     * Get OAuth access token.
     *
     * @since 1.0.0
     * @return string|null Access token or null on failure.
     */
    private function get_access_token(): ?string
    {
        $cached = get_transient('boostbot_paypal_token');
        if ($cached) {
            return $cached;
        }

        $response = wp_remote_post(
            $this->api_base . '/v1/oauth2/token',
            [
                'headers' => [
                    'Authorization' => 'Basic ' . base64_encode($this->client_id . ':' . $this->client_secret), // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- PayPal OAuth2 Basic auth
                    'Content-Type'  => 'application/x-www-form-urlencoded',
                ],
                'body'    => 'grant_type=client_credentials',
                'timeout' => 30,
            ]
        );

        if (is_wp_error($response)) {
            BoostBot_Logger::error('PayPal OAuth failed', ['error' => $response->get_error_message()]);
            return null;
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);

        if (isset($body['access_token'])) {
            $expires = ($body['expires_in'] ?? 3600) - 60; // Refresh 60s before expiry
            set_transient('boostbot_paypal_token', $body['access_token'], $expires);
            return $body['access_token'];
        }

        BoostBot_Logger::error('PayPal OAuth error', $body);
        return null;
    }

    /**
     * Make API request.
     *
     * @since 1.0.0
     * @param string $method   HTTP method.
     * @param string $endpoint API endpoint.
     * @param array  $data     Request data.
     * @return array Response array with 'success' and 'data' or 'error'.
     */
    private function request(string $method, string $endpoint, array $data = []): array
    {
        $token = $this->get_access_token();

        if (!$token) {
            return ['success' => false, 'error' => 'Failed to authenticate with PayPal'];
        }

        $args = [
            'method'  => $method,
            'headers' => [
                'Authorization' => 'Bearer ' . $token,
                'Content-Type'  => 'application/json',
            ],
            'timeout' => 30,
        ];

        if (!empty($data)) {
            $args['body'] = wp_json_encode($data);
        }

        $response = wp_remote_request($this->api_base . $endpoint, $args);

        if (is_wp_error($response)) {
            return ['success' => false, 'error' => $response->get_error_message()];
        }

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

        if ($code >= 200 && $code < 300) {
            return ['success' => true, 'data' => $body];
        }

        return [
            'success' => false,
            'error'   => $body['message'] ?? $body['error_description'] ?? 'Unknown error',
            'details' => $body,
        ];
    }

    /**
     * Create subscription.
     *
     * @since 1.0.0
     * @param string $plan_id    PayPal plan ID.
     * @param string $return_url Return URL after approval.
     * @param string $cancel_url Cancel URL.
     * @return array Response array.
     */
    public function create_subscription(string $plan_id, string $return_url, string $cancel_url): array
    {
        return $this->request(
            'POST',
            '/v1/billing/subscriptions',
            [
                'plan_id'            => $plan_id,
                'application_context' => [
                    'brand_name'          => 'BoostBot Pro',
                    'locale'              => 'he-IL',
                    'shipping_preference' => 'NO_SHIPPING',
                    'user_action'        => 'SUBSCRIBE_NOW',
                    'return_url'         => $return_url,
                    'cancel_url'         => $cancel_url,
                ],
            ]
        );
    }

    /**
     * Get subscription details.
     *
     * @since 1.0.0
     * @param string $subscription_id Subscription ID.
     * @return array Response array.
     */
    public function get_subscription(string $subscription_id): array
    {
        return $this->request('GET', '/v1/billing/subscriptions/' . $subscription_id);
    }

    /**
     * Cancel subscription.
     *
     * @since 1.0.0
     * @param string $subscription_id Subscription ID.
     * @param string $reason          Cancellation reason.
     * @return array Response array.
     */
    public function cancel_subscription(string $subscription_id, string $reason = ''): array
    {
        return $this->request(
            'POST',
            '/v1/billing/subscriptions/' . $subscription_id . '/cancel',
            [
                'reason' => $reason ?: 'Customer requested cancellation',
            ]
        );
    }

    /**
     * Get subscription transactions.
     *
     * @since 1.0.0
     * @param string      $subscription_id Subscription ID.
     * @param string|null $start_time      Start time (ISO 8601).
     * @return array Response array.
     */
    public function get_transactions(string $subscription_id, string $start_time = null): array
    {
        $start = $start_time ?? gmdate('Y-m-d', strtotime('-1 year')) . 'T00:00:00Z';
        $end = gmdate('Y-m-d') . 'T23:59:59Z';

        return $this->request(
            'GET',
            "/v1/billing/subscriptions/{$subscription_id}/transactions?start_time={$start}&end_time={$end}"
        );
    }

    /**
     * Verify webhook signature.
     *
     * @since 1.0.0
     * @param array  $headers Webhook headers.
     * @param string $body    Webhook body.
     * @return bool True if verified.
     */
    public function verify_webhook(array $headers, string $body): bool
    {
        $webhook_id = get_option('boostbot_paypal_webhook_id');

        if (empty($webhook_id)) {
            BoostBot_Logger::warning('PayPal webhook ID not configured');
            return false;
        }

        $result = $this->request(
            'POST',
            '/v1/notifications/verify-webhook-signature',
            [
                'transmission_id'  => $headers['PAYPAL-TRANSMISSION-ID'] ?? '',
                'transmission_time' => $headers['PAYPAL-TRANSMISSION-TIME'] ?? '',
                'cert_url'         => $headers['PAYPAL-CERT-URL'] ?? '',
                'auth_algo'        => $headers['PAYPAL-AUTH-ALGO'] ?? '',
                'transmission_sig' => $headers['PAYPAL-TRANSMISSION-SIG'] ?? '',
                'webhook_id'       => $webhook_id,
                'webhook_event'    => json_decode($body, true),
            ]
        );

        return $result['success'] && ($result['data']['verification_status'] ?? '') === 'SUCCESS';
    }
}

