<?php
/**
 * MakePay API client for Invision Commerce.
 */

namespace IPS\makepay\Api;

use IPS\makepay\Dpop\Dpop;
use IPS\makepay\Storage\Storage;

if (!\defined('\IPS\SUITE_UNIQUE_KEY')) {
    exit;
}

class _Client
{
    private string $baseUrl;

    public function __construct(?string $baseUrl = null)
    {
        $this->baseUrl = rtrim($baseUrl ?: 'https://www.makecrypto.io', '/');
    }

    public function createPaymentLink(string $companyId, array $payload): array
    {
        return $this->request(
            'POST',
            '/api/partner/v1/companies/' . rawurlencode($companyId) . '/payment-links',
            $payload,
        );
    }

    public function updateSettings(string $companyId, array $payload): array
    {
        return $this->request(
            'PUT',
            '/api/partner/v1/companies/' . rawurlencode($companyId) . '/products/makepay/settings',
            $payload,
        );
    }

    public function rotateWebhookSecret(string $companyId): array
    {
        return $this->request(
            'POST',
            '/api/partner/v1/companies/' . rawurlencode($companyId) . '/products/makepay/webhook-secret',
            [],
        );
    }

    public function extractPaymentUrl(array $response): string
    {
        foreach ([
            $response['paymentLink']['publicUrl'] ?? null,
            $response['paymentLink']['url'] ?? null,
            $response['publicUrl'] ?? null,
            $response['url'] ?? null,
            $response['paymentUrl'] ?? null,
        ] as $candidate) {
            if (is_string($candidate) && $candidate !== '') {
                return $candidate;
            }
        }

        throw new \RuntimeException('MakePay did not return a hosted checkout URL.');
    }

    public function request(string $method, string $path, ?array $payload = null): array
    {
        $url = $this->baseUrl . $path;
        $headers = [
            'accept: application/json',
            'content-type: application/json',
            'user-agent: MakePay-Invision/0.1.0',
        ];

        $accessToken = Storage::getSecret('oauth_access_token');
        if ($accessToken) {
            $privatePem = Storage::getSecret('dpop_private_pem') ?: '';
            $publicJwk = json_decode(Storage::get('dpop_public_jwk') ?: '[]', true) ?: [];
            $headers[] = 'authorization: DPoP ' . $accessToken;
            $headers[] = 'dpop: ' . Dpop::proof($privatePem, $publicJwk, $method, $url, $accessToken);
        } else {
            $keyId = Storage::get('api_key_id');
            $secret = Storage::getSecret('api_key_secret');
            if (!$keyId || !$secret) {
                throw new \RuntimeException('MakePay is not connected.');
            }
            $headers[] = 'x-api-key: ' . $keyId;
            $headers[] = 'x-api-secret: ' . $secret;
        }

        $response = $this->send($method, $url, $headers, $payload);
        if (($response['status'] ?? 500) === 401 && Storage::getSecret('oauth_refresh_token')) {
            \IPS\makepay\OAuth\OAuth::refreshToken();
            $response = $this->request($method, $path, $payload);
        }

        return $response;
    }

    public function send(string $method, string $url, array $headers, ?array $payload = null): array
    {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_CUSTOMREQUEST => strtoupper($method),
            CURLOPT_HTTPHEADER => $headers,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 30,
        ]);

        if ($payload !== null) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload, JSON_UNESCAPED_SLASHES));
        }

        $body = curl_exec($ch);
        $status = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        if ($body === false) {
            throw new \RuntimeException($error ?: 'MakePay API request failed.');
        }

        $decoded = json_decode((string) $body, true);
        if ($status < 200 || $status >= 300) {
            $message = is_array($decoded) && isset($decoded['error'])
                ? (string) $decoded['error']
                : "MakePay API responded with HTTP {$status}.";
            throw new \RuntimeException($message);
        }

        return is_array($decoded) ? $decoded : ['status' => $status, 'body' => $body];
    }
}
