<?php

namespace App\Service\ThirdParty;

use App\DTOs\BankAccountVerificationDto;
use App\DTOs\InitiatePaymentDto;
use App\DTOs\TransferFundsDto;
use App\DTOs\VirtualAccountDto;
use App\Service\ThirdParty\Interfaces\PaymentGatewayInterface;
use App\Service\ThirdParty\Interfaces\VerifyBankInterface;
use Exception;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class MonnifyService implements PaymentGatewayInterface
{
    protected string $baseUrl;
    protected string $contractCode;
    protected string $apiKey;
    protected string $clientId;
    protected string $clientSecret;

    public function __construct()
    {
        $this->baseUrl = config('thirdparty.monnify.base_url');
        $this->contractCode = config('thirdparty.monnify.contract_code');
        $this->apiKey = config('thirdparty.monnify.api_key');
        $this->clientId = config('thirdparty.monnify.client_id');
        $this->clientSecret = config('thirdparty.monnify.client_secret');
    }

    
    public function authenticate(): ?string
    {
        $url = $this->baseUrl . '/api/v1/auth/login';

        // Encode the API key and secret key in Base64
        $credentials = base64_encode($this->apiKey . ':' . $this->clientSecret);

        try {
            $response = Http::withHeaders([
                'Authorization' => 'Basic ' . $credentials,
            ])->post($url);

            if ($response->successful()) {
                $data = $response->json();
                return $data['responseBody']['accessToken'];
            }

            Log::error('Monnify Authentication Failed: ' . $response->body());
            return null;
        } catch (\Exception $e) {
            Log::error('Monnify Authentication Error: ' . $e->getMessage());
            return null;
        }
    }

    public function verifyBankAccount(array $accountDetails)
    {
        $accessToken = $this->authenticate();
        if (!$accessToken) {
            return $this->normalizeResponse(['status' => 'failed', 'message' => 'Failed to authenticate with Monnify.'], 'verify_bank_account');
        }

        $response = Http::withToken($accessToken)
            ->get("{$this->baseUrl}/api/v1/disbursements/account/validate", [
                'accountNumber' => $accountDetails['accountNumber'],
                'bankCode' => $accountDetails['bankCode'],
            ]);

        if ($response->failed()) {
            Log::error('Monnify account verification failed', ['response' => $response->json()]);
            return $this->normalizeResponse(['status' => 'failed', 'message' => 'Failed to verify bank account.'], 'verify_bank_account');
        }

        $data = $response->json('responseBody');
        return $this->normalizeResponse($data, 'verify_bank_account');
    }

    public function generateVirtualAccount(array $customerDetails): VirtualAccountDto
    {
        $accessToken = $this->authenticate();
        if (!$accessToken) {
            return VirtualAccountDto::failure('Failed to authenticate with Monnify.');
        }

        $response = Http::withToken($accessToken)
            ->post("{$this->baseUrl}/api/v2/bank-transfer/reserved-accounts", [
                'accountReference' => $customerDetails['accountReference'],
                'contractCode' => $this->contractCode,
                'customerName' => $customerDetails['customerName'],
                'customerEmail' => $customerDetails['customerEmail'],
                'bvn' => $customerDetails['bvn'],
                'getAllAvailableBanks' => true,
            ]);

        if ($response->failed()) {
            Log::error('Monnify virtual account creation failed', ['response' => $response->json()]);
            return $this->normalizeResponse(['status' => 'failed', 'message' => 'Failed to create virtual account.'], 'virtual_account');
        }

        $data = $response->json('responseBody');
        return $this->normalizeResponse($data, 'virtual_account');
    }

    public function transferFunds(array $transferDetails)
    {
        $accessToken = $this->authenticate();
        if (!$accessToken) {
            return $this->normalizeResponse(['status' => 'failed', 'message' => 'Failed to authenticate with Monnify.'], 'transfer_funds');
        }

        $response = Http::withToken($accessToken)
            ->post("{$this->baseUrl}/api/v1/disbursements/single", [
                'amount' => $transferDetails['amount'],
                'destinationAccountNumber' => $transferDetails['destinationAccount'],
                'destinationBankCode' => $transferDetails['bankCode'],
                'sourceAccountNumber' => $transferDetails['source_account'],
            ]);

        if ($response->failed()) {
            Log::error('Monnify transfer failed', ['response' => $response->json()]);
            return $this->normalizeResponse(['status' => 'failed', 'message' => 'Transfer failed.'], 'transfer_funds');
        }

        $data = $response->json('responseBody');
        return $this->normalizeResponse($data, 'transfer_funds');
    }

    public function initiatePayment(array $paymentDetails)
    {
        $accessToken = $this->authenticate();
        if (!$accessToken) {
            return $this->normalizeResponse(['status' => 'failed', 'message' => 'Failed to authenticate with Monnify.'], 'initiate_payment');
        }

        $response = Http::withToken($accessToken)
            ->post("{$this->baseUrl}/api/v1/merchant/transactions/init-transaction", [
                'amount' => $paymentDetails['amount'],
                'customerName' => $paymentDetails['customerName'],
                'customerEmail' => $paymentDetails['customerEmail'],
                'paymentReference' => $paymentDetails['paymentReference'],
                'contractCode' => $this->contractCode,
                'redirectUrl' => $paymentDetails['redirectUrl'] ?? null,
            ]);

        if ($response->failed()) {
            Log::error('Monnify payment initialization failed', ['response' => $response->json()]);
            return $this->normalizeResponse(['status' => 'failed', 'message' => 'Payment initialization failed.'], 'initiate_payment');
        }

        $data = $response->json('responseBody');
        return $this->normalizeResponse($data, 'initiate_payment');
    }

    public function checkTransactionStatus(string $transactionReference)
    {
        $accessToken = $this->authenticate();
        if (!$accessToken) {
            return $this->normalizeResponse(['status' => 'failed', 'message' => 'Failed to authenticate with Monnify.'], 'check_transaction_status');
        }

        $response = Http::withToken($accessToken)
            ->get("{$this->baseUrl}/api/v2/transactions/{$transactionReference}");

        if ($response->failed()) {
            Log::error('Monnify transaction status check failed', ['response' => $response->json()]);
            return $this->normalizeResponse(['status' => 'failed', 'message' => 'Failed to check transaction status.'], 'check_transaction_status');
        }

        $data = $response->json('responseBody');
        return $this->normalizeResponse($data, 'check_transaction_status');
    }

    protected function normalizeResponse(array $responseData, string $action): array
    {
        switch ($action) {
            case 'virtual_account':
                return [
                    'status' => $responseData['status'] === 'success' ? 'success' : 'failed',
                    'message' => $responseData['message'] ?? 'Virtual account created successfully',
                    'data' => [
                        'account_number' => $responseData['data']['virtual_account_number'] ?? 'Unknown',
                        'bank_name' => $responseData['data']['bank_name'] ?? 'Unknown',
                        'account_name' => $responseData['data']['account_name'] ?? 'Unknown',
                    ],
                ];
            case 'check_transaction_status':
                return [
                    'status' => $responseData['status'] === 'success' ? 'success' : 'failed',
                    'message' => $responseData['message'] ?? 'Transaction status retrieved successfully',
                    'data' => [
                        'transaction_reference' => $responseData['data']['transaction_reference'] ?? 'Unknown',
                        'status' => $responseData['data']['status'] ?? 'Unknown',
                        'amount' => $responseData['data']['amount'] ?? 0,
                        'currency' => $responseData['data']['currency'] ?? 'Unknown',
                    ],
                ];
            case 'verify_bank_account':
                return [
                    'status' => $responseData['status'] === 'success' ? 'success' : 'failed',
                    'message' => $responseData['message'] ?? 'Bank account verified successfully',
                    'data' => [
                        'account_number' => $responseData['data']['account_number'] ?? 'Unknown',
                        'account_name' => $responseData['data']['account_name'] ?? 'Unknown',
                        'bank_code' => $responseData['data']['bank_code'] ?? 'Unknown',
                    ],
                ];
            case 'transfer_funds':
                return [
                    'status' => $responseData['status'] === 'success' ? 'success' : 'failed',
                    'message' => $responseData['message'] ?? 'Funds transferred successfully',
                    'data' => [
                        'transfer_reference' => $responseData['data']['transfer_reference'] ?? 'Unknown',
                        'amount' => $responseData['data']['amount'] ?? 0,
                        'currency' => $responseData['data']['currency'] ?? 'Unknown',
                        'recipient' => $responseData['data']['recipient'] ?? 'Unknown',
                    ],
                ];
            case 'initiate_payment':
                return [
                    'status' => $responseData['status'] === 'success' ? 'success' : 'failed',
                    'message' => $responseData['message'] ?? 'Payment initiated successfully',
                    'data' => [
                        'payment_reference' => $responseData['data']['payment_reference'] ?? 'Unknown',
                        'amount' => $responseData['data']['amount'] ?? 0,
                        'currency' => $responseData['data']['currency'] ?? 'Unknown',
                    ],
                ];
            default:
                throw new \InvalidArgumentException("Unsupported action: {$action}");
        }
    }
}
