<?php
/**
 * Stripe Checkout + Webhook for Velora Monthly Access.
 * Uses secret key from payment_settings (Admin). No raw card data touches this server.
 */
class StripeService
{
    private string $secretKey;
    private string $webhookSecret;
    private string $publishableKey;
    private string $apiBase = 'https://api.stripe.com/v1';

    public function __construct(array $settings)
    {
        $this->secretKey = trim((string) ($settings['card_secret_key'] ?? ''));
        $this->webhookSecret = trim((string) ($settings['card_webhook_secret'] ?? ''));
        $this->publishableKey = trim((string) ($settings['card_publishable_key'] ?? ''));
    }

    public function isConfigured(): bool
    {
        return $this->secretKey !== '' && str_starts_with($this->secretKey, 'sk_');
    }

    public function publishableKey(): string
    {
        return $this->publishableKey;
    }

    /**
     * Create a Checkout Session for Monthly Access.
     * @return array{ok:bool,message:string,url?:string,session_id?:string,raw?:array}
     */
    public function createCheckoutSession(
        int $userId,
        string $email,
        string $name,
        float $amountKes,
        string $reference,
        string $successUrl,
        string $cancelUrl,
        ?int $planId = null
    ): array {
        if (!$this->isConfigured()) {
            return ['ok' => false, 'message' => 'Stripe is not configured. Add secret key (sk_...) in Admin → Payment integrations.'];
        }

        // Stripe expects amount in smallest currency unit. KES has no decimals in Stripe.
        $unitAmount = (int) max(1, round($amountKes));

        $params = [
            'mode' => 'payment',
            'success_url' => $successUrl . (str_contains($successUrl, '?') ? '&' : '?') . 'session_id={CHECKOUT_SESSION_ID}',
            'cancel_url' => $cancelUrl,
            'client_reference_id' => $reference,
            'customer_email' => $email,
            'line_items[0][price_data][currency]' => 'kes',
            'line_items[0][price_data][product_data][name]' => 'Velora Monthly Access',
            'line_items[0][price_data][product_data][description]' => '30 days full access + verified badge',
            'line_items[0][price_data][unit_amount]' => $unitAmount,
            'line_items[0][quantity]' => 1,
            'metadata[user_id]' => (string) $userId,
            'metadata[reference]' => $reference,
            'metadata[plan_id]' => $planId !== null ? (string) $planId : '',
            'payment_intent_data[metadata][user_id]' => (string) $userId,
            'payment_intent_data[metadata][reference]' => $reference,
        ];

        $result = ['ok' => false, 'message' => 'Stripe request failed'];
        for ($attempt = 1; $attempt <= 3; $attempt++) {
            $result = $this->request('POST', '/checkout/sessions', $params);
            if ($result['ok']) {
                break;
            }
            $msg = strtolower($result['message'] ?? '');
            if (!str_contains($msg, 'network') && !str_contains($msg, 'timeout') && ($result['http'] ?? 0) < 500) {
                break;
            }
            usleep(350000 * $attempt);
        }
        if (!$result['ok']) {
            return $result;
        }
        $data = $result['data'];
        $url = $data['url'] ?? null;
        $sid = $data['id'] ?? null;
        if (!$url || !$sid) {
            return ['ok' => false, 'message' => 'Stripe did not return a Checkout URL.', 'raw' => $data];
        }
        return [
            'ok' => true,
            'message' => 'Redirecting to Stripe Checkout',
            'url' => $url,
            'session_id' => $sid,
            'raw' => $data,
        ];
    }

    /**
     * Retrieve Checkout Session.
     * @return array{ok:bool,data?:array,message?:string}
     */
    public function retrieveSession(string $sessionId): array
    {
        if (!$this->isConfigured()) {
            return ['ok' => false, 'message' => 'Stripe not configured'];
        }
        return $this->request('GET', '/checkout/sessions/' . rawurlencode($sessionId));
    }

    /**
     * Verify Stripe-Signature header and return event payload or null.
     * @return array|null decoded event
     */
    public function constructEvent(string $payload, string $sigHeader): ?array
    {
        if ($this->webhookSecret === '') {
            // Without webhook secret we refuse to trust the body for activation.
            return null;
        }
        $parts = [];
        foreach (explode(',', $sigHeader) as $piece) {
            $piece = trim($piece);
            if (str_contains($piece, '=')) {
                [$k, $v] = explode('=', $piece, 2);
                $parts[$k][] = $v;
            }
        }
        $timestamp = $parts['t'][0] ?? null;
        $signatures = $parts['v1'] ?? [];
        if (!$timestamp || !$signatures) {
            return null;
        }
        // Reject stale timestamps (>5 minutes)
        if (abs(time() - (int) $timestamp) > 300) {
            return null;
        }
        $signed = $timestamp . '.' . $payload;
        $expected = hash_hmac('sha256', $signed, $this->webhookSecret);
        $valid = false;
        foreach ($signatures as $sig) {
            if (hash_equals($expected, $sig)) {
                $valid = true;
                break;
            }
        }
        if (!$valid) {
            return null;
        }
        $event = json_decode($payload, true);
        return is_array($event) ? $event : null;
    }

    /**
     * @return array{ok:bool,data?:array,message?:string,http?:int}
     */
    private function request(string $method, string $path, array $form = []): array
    {
        $url = $this->apiBase . $path;
        $ch = curl_init($url);
        $headers = [
            'Authorization: Bearer ' . $this->secretKey,
            'Content-Type: application/x-www-form-urlencoded',
        ];
        $opts = [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 45,
            CURLOPT_HTTPHEADER => $headers,
        ];
        if ($method === 'POST') {
            $opts[CURLOPT_POST] = true;
            $opts[CURLOPT_POSTFIELDS] = http_build_query($form);
        } elseif ($method !== 'GET') {
            $opts[CURLOPT_CUSTOMREQUEST] = $method;
            if ($form) {
                $opts[CURLOPT_POSTFIELDS] = http_build_query($form);
            }
        }
        curl_setopt_array($ch, $opts);
        $raw = curl_exec($ch);
        $http = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $err = curl_error($ch);
        curl_close($ch);
        if ($raw === false) {
            return ['ok' => false, 'message' => 'Network error contacting Stripe: ' . $err, 'http' => $http];
        }
        $data = json_decode($raw, true);
        if (!is_array($data)) {
            return ['ok' => false, 'message' => 'Invalid Stripe response', 'http' => $http];
        }
        if ($http >= 400) {
            $msg = $data['error']['message'] ?? ('Stripe error HTTP ' . $http);
            return ['ok' => false, 'message' => $msg, 'data' => $data, 'http' => $http];
        }
        return ['ok' => true, 'data' => $data, 'http' => $http];
    }
}
