<?php

namespace portalpeople\stripe\components;

use portalpeople\stripe\Module;
use RuntimeException;
use Yii;
use yii\base\Component;
use yii\httpclient\Client;
use yii\httpclient\Exception as HttpClientException;

class StripeClient extends Component
{
    private Client $http;
    private string $secretKey;
    private bool $debug;
    private int $timeout;
    private ?string $stripeVersion;

    public function __construct(
        string $secretKey,
        string $baseUrl,
        int $timeout,
        bool $debug,
        ?string $stripeVersion = null,
        array $config = []
    ) {
        $this->secretKey = $secretKey;
        $this->timeout = $timeout;
        $this->debug = $debug;
        $this->stripeVersion = $stripeVersion;

        $this->http = new Client([
            'baseUrl' => rtrim($baseUrl, '/'),
        ]);

        parent::__construct($config);
    }

    public static function fromModule(Module $module): self
    {
        if ($module->secretKey === '') {
            throw new RuntimeException('Stripe secretKey is not configured.');
        }

        return new self(
            $module->secretKey,
            $module->apiBaseUrl,
            $module->timeout,
            $module->debug,
            $module->stripeVersion
        );
    }

    public function listCustomers(array $query = []): array
    {
        return $this->request('GET', '/customers', $query);
    }

    public function retrieveCustomer(string $customerId): array
    {
        return $this->request('GET', '/customers/' . urlencode($customerId));
    }

    public function createPaymentIntent(array $payload, ?string $idempotencyKey = null): array
    {
        return $this->request('POST', '/payment_intents', $payload, $idempotencyKey);
    }

    public function retrievePaymentIntent(string $paymentIntentId): array
    {
        return $this->request('GET', '/payment_intents/' . urlencode($paymentIntentId));
    }

    public function listRefunds(array $query = []): array
    {
        return $this->request('GET', '/refunds', $query);
    }

    public function createRefund(array $payload, ?string $idempotencyKey = null): array
    {
        return $this->request('POST', '/refunds', $payload, $idempotencyKey);
    }

    public function retrieveBalance(): array
    {
        return $this->request('GET', '/balance');
    }

    /**
     * Make a custom request to the Stripe API.
     * Public method for flexibility in calling arbitrary Stripe endpoints.
     */
    public function makeRequest(string $method, string $path, array $params = [], ?string $idempotencyKey = null): array
    {
        return $this->request($method, $path, $params, $idempotencyKey);
    }

    private function request(string $method, string $path, array $params = [], ?string $idempotencyKey = null): array
    {
        $headers = [
            'Authorization' => 'Bearer ' . $this->secretKey,
        ];

        if ($this->stripeVersion !== null && $this->stripeVersion !== '') {
            $headers['Stripe-Version'] = $this->stripeVersion;
        }

        if ($idempotencyKey !== null && $idempotencyKey !== '') {
            $headers['Idempotency-Key'] = $idempotencyKey;
        }

        if ($this->debug) {
            Yii::debug('[stripe] ' . $method . ' ' . $path, __CLASS__);
        }

        try {
            $request = $this->http
                ->createRequest()
                ->setMethod($method)
                ->setUrl($path)
                ->setHeaders($headers)
                ->setOptions(['timeout' => $this->timeout]);

            if ($method === 'GET') {
                $request->setData($params);
            } else {
                $request->setHeaders(array_merge($headers, [
                    'Content-Type' => 'application/x-www-form-urlencoded',
                ]));
                $request->setData($params);
            }

            $response = $request->send();
        } catch (HttpClientException $e) {
            throw new RuntimeException('Stripe request failed: ' . $e->getMessage(), 0, $e);
        }

        $responseData = $response->getData();
        if (!$response->isOk) {
            $errorMessage = 'Stripe API error';
            if (is_array($responseData) && isset($responseData['error']['message'])) {
                $errorMessage = (string) $responseData['error']['message'];
            }
            throw new RuntimeException($errorMessage);
        }

        return is_array($responseData) ? $responseData : [];
    }
}
