<?php
namespace App\Helpers;

use GuzzleHttp\Client;

class Paystack
{
    /**
     * The base URL for the Paystack API.
     */
    protected static string $baseUrl = "https://api.paystack.co/";
    /**
     * The secret key for authenticating API requests.
     *
     * @var string
     */
    protected string $secretKey;

    /**
     * Create a new Paystack instance.
     *
     * @param string $secretKey The secret key for authenticating API requests.
     */
    public function __construct(string $secretKey)
    {
        $this->secretKey = $secretKey;
    }

    public function listNigerianBanks(): array
    {
        $response = $this->sendRequest('GET', 'bank');

        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'banks' => $response['data'],
        ];
    }

    /**
     * Verify a Paystack transaction.
     *
     * @param string $transaction The transaction reference to verify.
     * @return array An array containing the status, message, bill response, authorization code, channel, card type, bank, and reusable flag of the transaction.
     */
    public function verifyTransaction($reference): array
    {
        $response = $this->sendRequest('GET', "transaction/verify/{$reference}");
        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'bill_response' => $response['data']['gateway_response'],
            'authorization' => $response['data']['authorization']['authorization_code'],
            'channel' => $response['data']['authorization']['channel'],
            'card' => $response['data']['authorization']['card_type'],
            'bank' => $response['data']['authorization']['bank'],
            'reusable' => $response['authorization']['reusable'],
        ];
    }

    public function fetchTransaction($transaction): array
    {
        $response = $this->sendRequest('GET', "transaction/{$transaction}");
        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'bill_response' => $response['data']['gateway_response'],
            'customer' => $response['data']['customer']['customer_code'],
            'authorization' => $response['data']['authorization']['authorization_code'] ?? '',
            'channel' => $response['data']['authorization']['channel'],
            'card' => $response['data']['authorization']['card_type'] ?? '',
            'bank' => $response['data']['authorization']['bank'],
            'reusable' => $response['data']['authorization']['reusable'] ?? '',
        ];
    }

    /**
     * Charge a Paystack token.
     *
     * @param int $amount The amount to charge in kobo.
     * @param string $email The email of the user being charged.
     * @param string $token The Paystack token to charge.
     * @return array An array containing the status, message, and bill response of the charge.
     */
    public function chargeToken(int $amount, string $email, string $token): array
    {
        $fields = [
            'email' => $email,
            'amount' => $amount,
            'authorization_code' => $token,
        ];

        $response = $this->sendRequest('POST', 'transaction/charge_authorization', $fields);
        return [
            'status' => $response['data']['status'],
            'message' => $response['message'],
            'bill_response' => $response['data']['gateway_response'],
        ];
    }

    /**
     * Perform a partial debt on a Paystack transaction.
     *
     * @param string $email The email of the user being charged.
     * @param int $amount The amount to charge in kobo.
     * @param int $minimum The minimum amount allowed for the partial debt in kobo.
     * @param string $token The Paystack token to charge.
     * @return array An array containing the status, message, and bill response of the partial debt.
     */
    public function partialDebt(string $email, int $amount, int $minimum, string $token): array
    {
        $fields = [
            'email' => $email,
            'amount' => $amount,
            'at_least' => $minimum,
            'authorization_code' => $token,
            'currency' => 'NGN',
        ];

        $response = $this->sendRequest('POST', 'transaction/partial_debit', $fields);

        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'bill_response' => $response['data']['gateway_response'],
        ];
    }

    public function createTransferRecipient(string $type, string $name, array $details): array
    {
        $fields = [
            'type' => $type,
            'name' => $name,
            'details' => $details,
        ];

        $response = $this->sendRequest('POST', 'transferrecipient', $fields);

        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'bill_response' => $response['data']['gateway_response'],
        ];
    }

    /**
     * Perform a bulk create of transfer recipients.
     * @param array $recipients An array of recipients to create. Each recipient should be an associative array with the keys "type" (string), "name" (string), and "details" (array).
     * @return array An array containing the status, message, and bill response of the bulk recipient creation.
     */
    public function bulkCreateTransferRecipients(array $recipients): array
    {
        $fields = [
            'recipients' => $recipients,
        ];

        $response = $this->sendRequest('POST', 'transferrecipient/bulk', $fields);

        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'bill_response' => $response['data']['gateway_response'],
        ];
    }

    /**
     * Perform a single transfer.
     * @param int $amount The amount to transfer in kobo.
     * @param string $recipient The recipient code for the transfer.
     * @param string $reason The reason for the transfer.
     * @return array An array containing the status, message, and bill response of the transfer. 
     */

     public function singleTransfer(int $amount, string $recipient, string $reason): array
     {
         $fields = [
             'amount' => $amount,
             'recipient' => $recipient,
             'reason' => $reason,
         ];
 
         $response = $this->sendRequest('POST', 'transfer', $fields);
 
         return [
             'status' => $response['status'],
             'message' => $response['message'],
             'bill_response' => $response['data']['gateway_response'],
         ];
     }

     /**
     * Perform a bulk transfer.
     * @param array $transfers An array of transfers to perform. Each transfer should be an associative array with the keys "amount" (int), "recipient" (string), and "reason" (string).
     * @return array An array containing the status, message, and bill response of the bulk transfer.
     */
    public function bulkTransfer(array $transfers): array
    {
        $fields = [
            'transfers' => $transfers,
        ];

        $response = $this->sendRequest('POST', 'bulk_transfer', $fields);

        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'bill_response' => $response['data']['gateway_response'],
        ];
    }

    /**
     * Verify an account number.
     * @param string $accountNumber The account number to verify.
     * @param string $bankCode The bank code for the account.
     * @return array An array containing the status, message, and bill response of the account verification.
     */
    public function verifyAccountNumber(string $accountNumber, string $bankCode): array
    {
        $fields = [
            'account_number' => $accountNumber,
            'bank_code' => $bankCode,
        ];

        $response = $this->sendRequest('GET', 'bank/resolve', $fields);

        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'bill_response' => $response['data']['gateway_response'],
        ];
    }

    /**
     * Validate a customer.
     * @param string $customerId The customer ID to validate.
     * @return array An array containing the status, message, and bill response of the customer validation.
     */
    public function validateCustomer(string $customerId): array
    {
        $response = $this->sendRequest('GET', 'customer/' . $customerId);

        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'bill_response' => $response['data']['gateway_response'],
        ];
    }

    /**
     * Resolve a card BIN.
     * @param string $bin The BIN to resolve.
     * @return array An array containing the status, message, and bill response of the BIN resolution.
     */
    public function resolveCardBin(string $bin): array
    {
        $response = $this->sendRequest('GET', 'decision/bin/' . $bin);

        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'bill_response' => $response['data']['gateway_response'],
        ];
    }

    /**
     * Create a dedicated virtual account.
     *
     * @param string $type The type of account to create (e.g. "nuban").
     * @param string $name The name of the account.
     * @param string $description A description for the account.
     * @return array An array containing the status, message, and bill response of the account creation.
     */
    public function createDedicatedVirtualAccount(string $type, string $name, string $description): array
    {
        $fields = [
            'type' => $type,
            'name' => $name,
            'description' => $description,
        ];

        $response = $this->sendRequest('POST', 'dva/create', $fields);

        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'bill_response' => $response['data']['gateway_response'],
        ];
    }

    /**
     * Assign a dedicated virtual account to a customer.
     * @param string $customerEmail The email of the customer to assign the account to.
     * @param string $accountNumber The account number for the virtual account.
     * @return array An array containing the status, message, and bill response of the account assignment.
     */
    public function assignDedicatedVirtualAccount(string $customerEmail, string $accountNumber): array
    {
        $fields = [
            'customer' => $customerEmail,
            'account_number' => $accountNumber,
        ];

        $response = $this->sendRequest('POST', 'account/assign', $fields);

        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'bill_response' => $response['data']['gateway_response'],
        ];
    }

    /**
     * List dedicated accounts.
     * @return array An array containing the status, message, and a list of dedicated accounts.
     */
    public function listDedicatedAccounts(): array
    {
        $response = $this->sendRequest('GET', 'account');

        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'dedicated_accounts' => $response['data'],
        ];
    }

    /**
     * Deactivate a dedicated account.
     * @param string $accountId The ID of the dedicated account to deactivate.
     * @return array An array containing the status, message, and bill response of the deactivation.
     */
    public function deactivateDedicatedAccount(string $accountId): array
    {
        $response = $this->sendRequest('DELETE', 'account/' . $accountId);

        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'bill_response' => $response['data']['gateway_response'],
        ];
    }

    /**
     * Fetch a dedicated account.
     * @param string $accountId The ID of the dedicated account to fetch.
     * @return array An array containing the status, message, and dedicated account data.
     */
    public function fetchDedicatedAccount(string $accountId): array
    {
        $response = $this->sendRequest('GET', 'account/' . $accountId);

        return [
            'status' => $response['status'],
            'message' => $response['message'],
            'dedicated_account' => $response['data'],
        ];
    }

    protected function sendRequest($method, $endpoint, $fields = []): array
    {
        $client = new Client;

        $config = array(
            'headers' => [
                'Authorization' => "Bearer {$this->secretKey}",
                'Cache-Control' => 'no-cache',
            ],
            'verify' => false, // Disable SSL verification
        );

        if ($method === 'POST' || $method === 'PUT') {
            $config = array(
                'headers' => [
                    'Authorization' => "Bearer {$this->secretKey}",
                    'Cache-Control' => 'no-cache',
                    'Content-Type' => 'application/json',
                ],
                'json' => $fields,
                'verify' => false, // Disable SSL verification
            );
        }

        $paystackUrl = self::$baseUrl;
        $response = $client->request($method, "{$paystackUrl}{$endpoint}", $config);
        $responseArray = json_decode($response->getBody(), true);
        
        if($response->getStatusCode() == 200){
            return $responseArray;
        }

        return array();
    }
}
