<?php

declare(strict_types=1);

namespace FatBottom\Services;

use FatBottom\Core\Config;
use RuntimeException;

final class PaymentGateway
{
    public function __construct(private readonly Config $config)
    {
    }

    public function charge(string $sourceId, int $amountCents, string $orderNumber): array
    {
        if (!(bool) $this->config->get('square.enabled', false)) {
            return [
                'success' => true,
                'provider' => 'demo',
                'reference' => 'DEMO-' . strtoupper(bin2hex(random_bytes(5))),
                'status' => 'COMPLETED',
            ];
        }

        if ($sourceId === '') {
            throw new RuntimeException('Card details are required.');
        }
        if (!function_exists('curl_init')) {
            throw new RuntimeException('The PHP cURL extension is required for Square payments.');
        }

        $environment = (string) $this->config->get('square.environment', 'sandbox');
        $baseUrl = $environment === 'production'
            ? 'https://connect.squareup.com'
            : 'https://connect.squareupsandbox.com';
        $accessToken = (string) $this->config->get('square.access_token');
        $locationId = (string) $this->config->get('square.location_id');

        if ($accessToken === '' || $locationId === '') {
            throw new RuntimeException('Square is enabled but its server credentials are incomplete.');
        }

        $payload = json_encode([
            'source_id' => $sourceId,
            'idempotency_key' => substr(hash('sha256', 'fatbottom-payment|' . $orderNumber), 0, 45),
            'amount_money' => [
                'amount' => $amountCents,
                'currency' => 'USD',
            ],
            'location_id' => $locationId,
            'reference_id' => $orderNumber,
            'note' => 'Online pickup order ' . $orderNumber,
            'autocomplete' => true,
        ], JSON_THROW_ON_ERROR);

        $curl = curl_init($baseUrl . '/v2/payments');
        curl_setopt_array($curl, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $payload,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $accessToken,
                'Content-Type: application/json',
                'Square-Version: 2026-05-20',
            ],
            CURLOPT_TIMEOUT => 30,
        ]);
        $response = curl_exec($curl);
        $status = (int) curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
        $error = curl_error($curl);
        curl_close($curl);

        $decoded = is_string($response) ? json_decode($response, true) : null;
        if ($response === false || $status < 200 || $status >= 300 || !isset($decoded['payment'])) {
            $message = $decoded['errors'][0]['detail'] ?? $error ?: 'Payment was declined.';
            throw new RuntimeException((string) $message);
        }

        return [
            'success' => true,
            'provider' => 'square',
            'reference' => (string) $decoded['payment']['id'],
            'status' => (string) $decoded['payment']['status'],
        ];
    }

    public function refund(string $paymentReference, int $amountCents, string $reason): array
    {
        if (!(bool) $this->config->get('square.enabled', false) || str_starts_with($paymentReference, 'DEMO-')) {
            return ['success' => true, 'reference' => 'REF-DEMO-' . strtoupper(bin2hex(random_bytes(4)))];
        }

        if (!function_exists('curl_init')) {
            throw new RuntimeException('The PHP cURL extension is required for Square refunds.');
        }

        $environment = (string) $this->config->get('square.environment', 'sandbox');
        $baseUrl = $environment === 'production'
            ? 'https://connect.squareup.com'
            : 'https://connect.squareupsandbox.com';
        $payload = json_encode([
            'idempotency_key' => bin2hex(random_bytes(16)),
            'payment_id' => $paymentReference,
            'amount_money' => ['amount' => $amountCents, 'currency' => 'USD'],
            'reason' => substr($reason, 0, 190),
        ], JSON_THROW_ON_ERROR);

        $curl = curl_init($baseUrl . '/v2/refunds');
        curl_setopt_array($curl, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => $payload,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $this->config->get('square.access_token'),
                'Content-Type: application/json',
                'Square-Version: 2026-05-20',
            ],
            CURLOPT_TIMEOUT => 30,
        ]);
        $response = curl_exec($curl);
        $status = (int) curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
        curl_close($curl);
        $decoded = is_string($response) ? json_decode($response, true) : null;
        if ($status < 200 || $status >= 300 || !isset($decoded['refund'])) {
            throw new RuntimeException((string) ($decoded['errors'][0]['detail'] ?? 'Refund failed.'));
        }

        return ['success' => true, 'reference' => (string) $decoded['refund']['id']];
    }
}
