<?php

namespace App\Services;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Support\Facades\Log;

class MpesaService
{
    protected $consumerKey;
    protected $consumerSecret;
    protected $shortcode;
    protected $passkey;
    protected $callbackUrl;
    protected $environment;
    
    public function __construct()
    {
        $this->consumerKey = config('services.mpesa.consumer_key');
        $this->consumerSecret = config('services.mpesa.consumer_secret');
        $this->shortcode = config('services.mpesa.shortcode');
        $this->passkey = config('services.mpesa.passkey');
        $this->callbackUrl = config('services.mpesa.callback_url');
        $this->environment = config('services.mpesa.environment');
    }
    
    /**
     * Get Access Token from M-Pesa API
     */
    public function getAccessToken()
    {
        $client = new Client();
        
        try {
            $response = $client->get(config('services.mpesa.auth_url'), [
                'auth' => [$this->consumerKey, $this->consumerSecret],
                'headers' => [
                    'Accept' => 'application/json',
                ],
                'verify' => false,
                'timeout' => 30,
            ]);
            
            $data = json_decode($response->getBody()->getContents());
            
            if (isset($data->access_token)) {
                return $data->access_token;
            }
            
            Log::error('M-Pesa Token Error: No access token in response', (array)$data);
            return null;
            
        } catch (RequestException $e) {
            Log::error('M-Pesa Token Request Error: ' . $e->getMessage());
            return null;
        } catch (\Exception $e) {
            Log::error('M-Pesa Token General Error: ' . $e->getMessage());
            return null;
        }
    }
    
    /**
     * Format phone number to M-Pesa format (2547XXXXXXXX)
     */
    private function formatPhoneNumber($phone)
    {
        if (empty($phone)) {
            return '';
        }
        
        // Remove any non-numeric characters - using manual method to avoid regex issues
        $cleaned = '';
        for ($i = 0; $i < strlen($phone); $i++) {
            $char = $phone[$i];
            if (is_numeric($char)) {
                $cleaned .= $char;
            }
        }
        $phone = $cleaned;
        
        // If starts with 0, replace with 254
        if (strlen($phone) > 0 && $phone[0] == '0') {
            $phone = '254' . substr($phone, 1);
        }
        
        // If starts with 254, return as is
        if (strlen($phone) >= 3 && substr($phone, 0, 3) == '254') {
            return $phone;
        }
        
        // If 9 digits (without leading 0), prepend 254
        if (strlen($phone) == 9) {
            return '254' . $phone;
        }
        
        return $phone;
    }
    
    /**
     * Initiate STK Push Payment
     */
    public function stkPush($phone, $amount, $reference, $description = "Payment")
    {
        // Format phone number
        $phone = $this->formatPhoneNumber($phone);
        
        // Get access token
        $token = $this->getAccessToken();
        
        if (!$token) {
            return [
                'success' => false, 
                'message' => 'Failed to authenticate with M-Pesa. Please try again.'
            ];
        }
        
        // Generate timestamp (YYYYMMDDHHMMSS)
        $timestamp = date('YmdHis');
        
        // Generate password
        $password = base64_encode($this->shortcode . $this->passkey . $timestamp);
        
        $client = new Client();
        
        try {
            $response = $client->post(config('services.mpesa.stk_push_url'), [
                'headers' => [
                    'Authorization' => 'Bearer ' . $token,
                    'Content-Type' => 'application/json',
                ],
                'json' => [
                    'BusinessShortCode' => $this->shortcode,
                    'Password' => $password,
                    'Timestamp' => $timestamp,
                    'TransactionType' => 'CustomerPayBillOnline',
                    'Amount' => $amount,
                    'PartyA' => $phone,
                    'PartyB' => $this->shortcode,
                    'PhoneNumber' => $phone,
                    'CallBackURL' => $this->callbackUrl,
                    'AccountReference' => $reference,
                    'TransactionDesc' => $description
                ],
                'verify' => false,
                'timeout' => 30,
            ]);
            
            $result = json_decode($response->getBody()->getContents());
            
            // Log the response for debugging
            Log::info('M-Pesa STK Push Response:', (array)$result);
            
            // Check if request was successful
            if (isset($result->ResponseCode) && $result->ResponseCode == "0") {
                return [
                    'success' => true,
                    'message' => 'Payment request sent successfully',
                    'checkout_request_id' => $result->CheckoutRequestID,
                    'merchant_request_id' => $result->MerchantRequestID,
                    'customer_message' => $result->CustomerMessage ?? 'Check your phone for payment prompt'
                ];
            } else {
                $errorMsg = $result->errorMessage ?? 'Failed to initiate payment';
                Log::error('M-Pesa STK Push Failed: ' . $errorMsg);
                
                return [
                    'success' => false,
                    'message' => $errorMsg
                ];
            }
            
        } catch (RequestException $e) {
            $errorMessage = 'Network error: ' . $e->getMessage();
            if ($e->hasResponse()) {
                $errorMessage = 'API Error: ' . $e->getResponse()->getBody()->getContents();
            }
            
            Log::error('M-Pesa STK Push Exception: ' . $errorMessage);
            
            return [
                'success' => false,
                'message' => 'Payment service is temporarily unavailable. Please try again.'
            ];
        } catch (\Exception $e) {
            Log::error('M-Pesa STK Push General Error: ' . $e->getMessage());
            
            return [
                'success' => false,
                'message' => 'An unexpected error occurred. Please try again.'
            ];
        }
    }
    
    /**
     * Check STK Push Status
     */
    public function checkStkStatus($checkoutRequestId)
    {
        $token = $this->getAccessToken();
        
        if (!$token) {
            return [
                'success' => false, 
                'message' => 'Failed to authenticate'
            ];
        }
        
        // Generate timestamp and password
        $timestamp = date('YmdHis');
        $password = base64_encode($this->shortcode . $this->passkey . $timestamp);
        
        $client = new Client();
        
        try {
            $response = $client->post(config('services.mpesa.stk_query_url'), [
                'headers' => [
                    'Authorization' => 'Bearer ' . $token,
                    'Content-Type' => 'application/json',
                ],
                'json' => [
                    'BusinessShortCode' => $this->shortcode,
                    'Password' => $password,
                    'Timestamp' => $timestamp,
                    'CheckoutRequestID' => $checkoutRequestId
                ],
                'verify' => false,
                'timeout' => 30,
            ]);
            
            $result = json_decode($response->getBody()->getContents());
            
            Log::info('M-Pesa STK Status Check:', (array)$result);
            
            return $result;
            
        } catch (\Exception $e) {
            Log::error('M-Pesa Status Check Error: ' . $e->getMessage());
            return null;
        }
    }
    
    /**
     * Validate M-Pesa Callback
     */
    public function validateCallback($callbackData)
    {
        // This is a basic validation
        // In production, you should verify the signature
        
        if (!isset($callbackData->Body->stkCallback)) {
            return false;
        }
        
        $stkCallback = $callbackData->Body->stkCallback;
        
        if (!isset($stkCallback->CheckoutRequestID) || !isset($stkCallback->ResultCode)) {
            return false;
        }
        
        return true;
    }
}