<?php

namespace App\Http\Controllers;

use App\Models\GatewayTransaction;
use App\Models\Transfer;
use App\Models\UserTransaction;
use App\Services\GatewayWebhookService;
use App\Services\PalmPayService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class PalmPayWebhookController extends Controller
{
    public function handle(Request $request, PalmPayService $palmPay, GatewayWebhookService $webhooks)
    {
        $payload = $request->all();

        $signature = $payload['sign'] ?? null;
        if (!is_string($signature) || $signature === '') {
            return response('missing_signature', 400);
        }

        unset($payload['sign']);

        try {
            $ok = $palmPay->verifyWebhook($payload, $signature);
        } catch (\Throwable $e) {
            return response('invalid_signature', 400);
        }

        if (!$ok) {
            return response('invalid_signature', 400);
        }

        $orderId = $payload['orderId'] ?? null;
        $orderNo = $payload['orderNo'] ?? null;

        $query = GatewayTransaction::query();
        if (is_string($orderId) && $orderId !== '') {
            $query->where('palmpay_order_id', $orderId);
        }
        if (is_string($orderNo) && $orderNo !== '') {
            if (is_string($orderId) && $orderId !== '') {
                $query->orWhere('palmpay_order_no', $orderNo);
            } else {
                $query->where('palmpay_order_no', $orderNo);
            }
        }

        $tx = $query->first();

        if ($tx) {
            $status = $this->mapPalmPayStatus($payload['orderStatus'] ?? null);
            $tx->update([
                'status' => $status,
                'paid_at' => $status === 'success' ? ($tx->paid_at ?? now()) : $tx->paid_at,
                'palmpay_order_id' => is_string($orderId) && $orderId !== '' ? $orderId : $tx->palmpay_order_id,
                'palmpay_order_no' => is_string($orderNo) && $orderNo !== '' ? $orderNo : $tx->palmpay_order_no,
                'raw_provider_webhook' => $request->all(),
            ]);

            if ($status === 'success') {
                $webhooks->dispatch($tx->fresh(), 'payment.success');
            }
        }

        $transferQuery = Transfer::query();
        if (is_string($orderId) && $orderId !== '') {
            $transferQuery->where('reference', $orderId);
        }
        if (is_string($orderNo) && $orderNo !== '') {
            if (is_string($orderId) && $orderId !== '') {
                $transferQuery->orWhere('transfer_id', $orderNo);
            } else {
                $transferQuery->where('transfer_id', $orderNo);
            }
        }

        $transfer = $transferQuery->first();

        if ($transfer) {
            $this->applyTransferStatus($transfer, $this->mapPalmPayTransferStatus($payload['orderStatus'] ?? null), $request->all());
        }

        return response('success', 200);
    }

    private function mapPalmPayStatus($orderStatus): string
    {
        if (!is_numeric($orderStatus)) {
            return 'unknown';
        }

        return match ((int) $orderStatus) {
            0 => 'unpaid',
            1 => 'paying',
            2 => 'success',
            3 => 'failed',
            4 => 'closed',
            default => 'unknown',
        };
    }

    private function mapPalmPayTransferStatus($orderStatus): string
    {
        if (!is_numeric($orderStatus)) {
            return Transfer::STATUS_PENDING;
        }

        return match ((int) $orderStatus) {
            2 => Transfer::STATUS_SUCCESS,
            3, 4 => Transfer::STATUS_FAILED,
            default => Transfer::STATUS_PENDING,
        };
    }

    private function applyTransferStatus(Transfer $transfer, string $status, array $payload): void
    {
        $previousStatus = $transfer->status;

        DB::transaction(function () use ($transfer, $status, $payload, $previousStatus) {
            $transfer->update([
                'status' => $status,
                'last_status_check_at' => now(),
                'response_data' => array_merge($transfer->response_data ?? [], [
                    'provider' => 'palmpay',
                    'webhook' => $payload,
                ]),
            ]);

            $transaction = UserTransaction::query()
                ->where('user_id', $transfer->user_id)
                ->where('reference', $transfer->reference)
                ->where('source', UserTransaction::SOURCE_BANK_TRANSFER)
                ->latest('id')
                ->first();

            if ($transaction) {
                $mappedStatus = match ($status) {
                    Transfer::STATUS_SUCCESS => UserTransaction::STATUS_SUCCESSFUL,
                    Transfer::STATUS_FAILED => UserTransaction::STATUS_FAILED,
                    default => UserTransaction::STATUS_PENDING,
                };

                if ($transaction->status !== $mappedStatus) {
                    $transaction->update(['status' => $mappedStatus]);
                }
            }

            if ($status === Transfer::STATUS_FAILED && $previousStatus !== Transfer::STATUS_FAILED) {
                $transfer->user->increment('wallet_balance', $transfer->amount + $transfer->fee);

                UserTransaction::create([
                    'user_id' => $transfer->user_id,
                    'type' => UserTransaction::TYPE_CREDIT,
                    'amount' => $transfer->amount + $transfer->fee,
                    'source' => UserTransaction::SOURCE_BANK_TRANSFER,
                    'status' => UserTransaction::STATUS_SUCCESSFUL,
                    'message' => "Refunded N{$transfer->amount + $transfer->fee} to your account due to failed transfer",
                ]);
            }
        });
    }
}
