<?php

namespace App\Http\Controllers\Webhook;

use App\Http\Controllers\Controller;
use App\Models\Transaction;
use App\Models\WebhookEvent;
use App\Services\SystemSettingService;
use App\Services\TransactionService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

class SmeplugWebhookController extends Controller
{
    public function __invoke(
        Request $request,
        TransactionService $transactionService,
        SystemSettingService $systemSettingService
    ): JsonResponse
    {
        $this->assertSecret($request, $systemSettingService);

        $payload = $request->all();
        $eventReference = $this->eventReference($payload);

        $event = WebhookEvent::firstOrCreate(
            ['event_reference' => $eventReference],
            [
                'provider' => 'smeplug',
                'payload_json' => $payload,
            ]
        );

        if ($event->processed_at !== null) {
            return response()->json(['ok' => true, 'duplicate' => true]);
        }

        $customerReference = (string) (data_get($payload, 'transaction.customer_reference') ?? '');
        $vendorReference = (string) (data_get($payload, 'transaction.reference') ?? '');

        $transaction = Transaction::query()
            ->when($customerReference !== '', fn ($q) => $q->where('internal_reference', $customerReference))
            ->when($customerReference === '' && $vendorReference !== '', fn ($q) => $q->where('vendor_reference', $vendorReference))
            ->first();

        if (! $transaction) {
            $event->update(['processed_at' => now()]);

            return response()->json(['ok' => true, 'matched' => false], 202);
        }

        $status = (string) (data_get($payload, 'transaction.status') ?? data_get($payload, 'status') ?? 'pending');
        $updated = $transactionService->applyWebhookStatusUpdate($transaction, $status, $payload);

        $event->update(['processed_at' => now()]);

        return response()->json([
            'ok' => true,
            'matched' => true,
            'transaction_id' => $updated->id,
            'status' => $updated->status,
        ]);
    }

    protected function assertSecret(Request $request, SystemSettingService $systemSettingService): void
    {
        $expected = (string) $systemSettingService->get(
            'webhooks.smeplug',
            'secret',
            config('vtu.webhooks.smeplug.secret', '')
        );

        if ($expected === '') {
            if (app()->environment(['local', 'testing'])) {
                return;
            }

            abort(503, 'SMEPlug webhook secret is not configured.');
        }

        $provided = (string) (
            $request->header('X-Webhook-Secret')
            ?? $request->header('X-SMEPlug-Secret')
            ?? $request->query('secret')
            ?? ''
        );

        abort_unless(hash_equals($expected, $provided), 401, 'Invalid webhook secret.');
    }

    protected function eventReference(array $payload): string
    {
        $reference = (string) (data_get($payload, 'transaction.reference')
            ?? data_get($payload, 'transaction.customer_reference')
            ?? '');

        if ($reference !== '') {
            return 'smeplug:'.$reference;
        }

        return 'smeplug:'.hash('sha256', json_encode($payload));
    }
}
