<?php

namespace App\Services;

use Illuminate\Http\Client\Factory;
use Illuminate\Support\Facades\Http;

/**
 * PaystackService
 *
 * Handles Paystack payment gateway integration including transaction verification,
 * webhook signature validation, and inline payment configuration.
 *
 * Requirements: 1.4, 1.5, 1.6, 1.7, 11.2
 */
class PaystackService
{
    const VERIFY_URL = 'https://api.paystack.co/transaction/verify/';

    protected Factory $httpClient;

    public function __construct()
    {
        $this->httpClient = Http::class;
    }

    /**
     * Verify a transaction with Paystack API
     *
     * @param string $reference Transaction reference
     * @param string $secretKey Paystack secret key
     * @return array{status: string, data: array}
     * @throws \Exception
     */
    public function verifyTransaction(string $reference, string $secretKey): array
    {
        try {
            $response = Http::withToken($secretKey)->get(
                self::VERIFY_URL . $reference
            );

            if (!$response->successful()) {
                return [
                    'status' => 'failed',
                    'message' => 'Paystack API error: ' . $response->status(),
                ];
            }

            $data = $response->json();

            // Return status from Paystack response
            return [
                'status' => $data['status'] ?? 'failed',
                'data' => $data['data'] ?? [],
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => 'Verification error: ' . $e->getMessage(),
            ];
        }
    }

    /**
     * Validate Paystack webhook signature
     *
     * @param string $payload Raw request body
     * @param string $signature x-paystack-signature header value
     * @param string $webhookSecret Paystack webhook secret
     * @return bool
     */
    public function validateWebhookSignature(string $payload, string $signature, string $webhookSecret): bool
    {
        $hash = hash_hmac('sha512', $payload, $webhookSecret);
        return hash_equals($hash, $signature);
    }

    /**
     * Build Paystack Inline JS configuration
     *
     * @param string $publicKey Paystack public key
     * @param string $email Customer email
     * @param float $amount Amount in base units (KES, USD, etc.)
     * @param string $reference Unique transaction reference
     * @param string $currency ISO 4217 currency code
     * @param array $metadata Optional metadata
     * @return array
     */
    public function buildInlineConfig(
        string $publicKey,
        string $email,
        float $amount,
        string $reference,
        string $currency,
        array $metadata = []
    ): array {
        $smallestUnit = $this->toSmallestUnit($amount, $currency);

        return [
            'key' => $publicKey,
            'email' => $email,
            'amount' => $smallestUnit,
            'reference' => $reference,
            'currency' => strtoupper($currency),
            'metadata' => $metadata,
            'onClose' => 'function() {}',
        ];
    }

    /**
     * Generate a unique payment reference with a prefix
     *
     * @param string $prefix Prefix for the reference (e.g., 'SP-ENR-', 'SP-CERT-')
     * @return string
     */
    public function generateReference(string $prefix): string
    {
        $timestamp = (string)now()->timestamp;
        $random = str_pad((string)random_int(0, 99999), 5, '0', STR_PAD_LEFT);
        return $prefix . $timestamp . $random;
    }

    /**
     * Convert amount to smallest unit based on currency
     *
     * Paystack expects amounts in the smallest unit:
     * - KES: 1 KES = 100 (multiply by 100)
     * - USD: 1 USD = 100 cents (multiply by 100)
     * - GHS: 1 GHS = 100 pesewas (multiply by 100)
     * - NGN: 1 NGN = 100 kobo (multiply by 100)
     * - ZAR: 1 ZAR = 100 cents (multiply by 100)
     *
     * @param float $amount
     * @param string $currency ISO 4217 code
     * @return int
     */
    public function toSmallestUnit(float $amount, string $currency): int
    {
        // Most currencies are subunits of 100
        // Except BTC, ETH, etc. which are handled separately
        $subunit = 100;

        return (int)($amount * $subunit);
    }
}
