<?php

namespace App\Services;

use App\Models\MpesaTransaction;
use App\Models\Payment;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class MpesaService
{
    private string $baseUrl;
    private string $consumerKey;
    private string $consumerSecret;
    private string $shortcode;
    private string $passkey;
    private string $callbackUrl;

    public function __construct()
    {
        $config = config('services.mpesa');
        $this->baseUrl = $config['env'] === 'production' 
            ? 'https://api.safaricom.co.ke' 
            : 'https://sandbox.safaricom.co.ke';
        
        $this->consumerKey = $config['consumer_key'] ?? '';
        $this->consumerSecret = $config['consumer_secret'] ?? '';
        $this->shortcode = $config['shortcode'] ?? '';
        $this->passkey = $config['passkey'] ?? '';
        $this->callbackUrl = $config['callback_url'] ?? '';
    }

    public function generateToken(): ?string
    {
        $response = Http::withBasicAuth($this->consumerKey, $this->consumerSecret)
            ->get($this->baseUrl . '/oauth/v1/generate?grant_type=client_credentials');

        if ($response->failed()) {
            Log::error('Mpesa Token Generation Failed', ['response' => $response->body()]);
            return null;
        }

        return $response->json('access_token');
    }

    public function initiateStkPush(Payment $payment, string $phoneNumber): bool
    {
        $token = $this->generateToken();
        if (!$token) return false;

        $timestamp = now()->format('YmdHis');
        $password = base64_encode($this->shortcode . $this->passkey . $timestamp);
        
        // Ensure phone starts with 254
        $phoneNumber = preg_replace('/^(0|\+)/', '254', $phoneNumber);
        if (!preg_match('/^254\d{9}$/', $phoneNumber)) {
            Log::warning('Invalid phone number for M-Pesa STK Push: ' . $phoneNumber);
            return false;
        }

        $payload = [
            'BusinessShortCode' => $this->shortcode,
            'Password' => $password,
            'Timestamp' => $timestamp,
            'TransactionType' => 'CustomerPayBillOnline',
            'Amount' => (int) round((float) $payment->amount),
            'PartyA' => $phoneNumber,
            'PartyB' => $this->shortcode,
            'PhoneNumber' => $phoneNumber,
            'CallBackURL' => $this->callbackUrl,
            'AccountReference' => 'Order-' . $payment->order?->order_number,
            'TransactionDesc' => 'Payment for order ' . $payment->order?->order_number,
        ];

        $response = Http::withToken($token)
            ->post($this->baseUrl . '/mpesa/stkpush/v1/query', $payload); // Note: Sandbox uses v1/query or v1/process? Usually v1/processrequest

        // Correction: Most STK Push use v1/processrequest
        $response = Http::withToken($token)
            ->post($this->baseUrl . '/mpesa/stkpush/v1/processrequest', $payload);

        if ($response->failed()) {
            Log::error('Mpesa STK Push Failed', ['response' => $response->body()]);
            return false;
        }

        $resData = $response->json();
        
        MpesaTransaction::create([
            'company_id' => $payment->order?->company_id,
            'branch_id' => $payment->order?->branch_id,
            'payment_id' => $payment->id,
            'order_id' => $payment->order_id,
            'merchant_request_id' => $resData['MerchantRequestID'],
            'checkout_request_id' => $resData['CheckoutRequestID'],
            'amount' => $payment->amount,
            'phone' => $phoneNumber,
            'status' => 'pending',
        ]);

        return true;
    }

    public function settleTransaction(array $payload): void
    {
        $stkCallback = $payload['Body']['stkCallback'] ?? null;
        if (!$stkCallback) return;

        $checkoutRequestId = $stkCallback['CheckoutRequestID'];
        $resultCode = $stkCallback['ResultCode'];

        $transaction = MpesaTransaction::where('checkout_request_id', $checkoutRequestId)->first();
        if (!$transaction) {
            Log::warning('Mpesa Callback: Transaction not found for CheckoutRequestID ' . $checkoutRequestId);
            return;
        }

        if ($resultCode === 0) {
            $callbackMetadata = $stkCallback['CallbackMetadata']['Item'] ?? [];
            $receipt = null;
            foreach ($callbackMetadata as $item) {
                if ($item['Name'] === 'MpesaReceiptNumber') {
                    $receipt = $item['Value'];
                    break;
                }
            }

            $transaction->update([
                'status' => 'completed',
                'receipt_number' => $receipt,
                'callback_payload' => $payload,
                'transaction_date' => now(),
            ]);

            $payment = $transaction->payment;
            if ($payment) {
                $payment->update([
                    'status' => 'completed',
                    'reference_number' => $receipt,
                    'processed_at' => now(),
                ]);

                // Update Order if necessary
                $order = $payment->order;
                if ($order) {
                    $order->refresh();
                    $totalPaid = $order->payments()->where('status', 'completed')->sum('amount');
                    $status = $totalPaid >= (float) $order->total_amount ? 'completed' : 'processing';
                    $order->update([
                        'amount_paid' => $totalPaid,
                        'status' => $status,
                    ]);
                }
            }
        } else {
            $transaction->update([
                'status' => 'failed',
                'callback_payload' => $payload,
            ]);
            
            if ($transaction->payment) {
                $transaction->payment->update(['status' => 'failed']);
            }
        }
    }
}
