<?php

namespace App\Support;

class PaymentGatewayInspector
{
    private const METHOD_LABELS = [
        'bkash' => 'bKash',
        'nagad' => 'Nagad',
        'rocket' => 'Rocket',
        'voucher' => 'Voucher',
        'payfastbd' => 'PayFastBD',
        'bank' => 'Bank',
        'card' => 'Card',
        'wallet' => 'Wallet',
    ];

    public static function extractTrxId(mixed $payload): ?string
    {
        if (is_string($payload)) {
            $text = trim($payload);
            if ($text === '') {
                return null;
            }

            $decoded = json_decode($text, true);
            if (is_array($decoded)) {
                $hit = self::extractTrxId($decoded);
                if ($hit !== null) {
                    return $hit;
                }
            }

            foreach ([
                '/\b(?:trx[\s_-]*id|txn[\s_-]*id|tx[\s_-]*id)\b[:#\s-]*([A-Za-z0-9-]+)/i',
                '/\b(?:merchant[\s_-]*invoice(?:[\s_-]*(?:no|number))?|payment[\s_-]*id|reference[\s_-]*id)\b[:#\s-]*([A-Za-z0-9-]+)/i',
            ] as $pattern) {
                if (preg_match($pattern, $text, $matches)) {
                    return trim((string) ($matches[1] ?? '')) ?: null;
                }
            }

            return null;
        }

        if (!is_array($payload)) {
            return null;
        }

        foreach ($payload as $key => $value) {
            $normalizedKey = self::normalizeKey($key);

            if (
                in_array($normalizedKey, [
                    'trxid',
                    'txid',
                    'paymenttrxid',
                    'merchantinvoice',
                    'merchantinvoiceno',
                    'merchantinvoicenumber',
                    'paymentid',
                    'referenceid',
                    'reference',
                    'banktrxid',
                ], true)
                && is_scalar($value)
            ) {
                $candidate = trim((string) $value);
                if ($candidate !== '') {
                    return $candidate;
                }
            }

            if (
                is_string($value)
                && in_array($normalizedKey, ['message', 'sms', 'note', 'details', 'description', 'info', 'statusmessage'], true)
            ) {
                $hit = self::extractTrxId($value);
                if ($hit !== null) {
                    return $hit;
                }
            }
        }

        foreach ($payload as $value) {
            $hit = self::extractTrxId($value);
            if ($hit !== null) {
                return $hit;
            }
        }

        return null;
    }

    public static function detectMethod(?string $storedMethod, mixed $payload): ?string
    {
        $detected = self::extractMethodFromMixed($payload);
        if ($detected !== null) {
            return $detected;
        }

        return self::normalizeMethodLabel($storedMethod);
    }

    private static function extractMethodFromMixed(mixed $payload): ?string
    {
        if (is_string($payload)) {
            $text = trim($payload);
            if ($text === '') {
                return null;
            }

            $decoded = json_decode($text, true);
            if (is_array($decoded)) {
                $hit = self::extractMethodFromMixed($decoded);
                if ($hit !== null) {
                    return $hit;
                }
            }

            return self::normalizeMethodLabel($text);
        }

        if (!is_array($payload)) {
            return null;
        }

        foreach ($payload as $key => $value) {
            $normalizedKey = self::normalizeKey($key);

            if (
                in_array($normalizedKey, ['paymentmethod', 'gateway', 'method', 'channel', 'provider', 'brand'], true)
                && is_scalar($value)
            ) {
                $hit = self::normalizeMethodLabel((string) $value);
                if ($hit !== null) {
                    return $hit;
                }
            }
        }

        foreach ($payload as $value) {
            $hit = self::extractMethodFromMixed($value);
            if ($hit !== null) {
                return $hit;
            }
        }

        return null;
    }

    private static function normalizeMethodLabel(?string $value): ?string
    {
        $text = strtolower(trim((string) $value));
        if ($text === '') {
            return null;
        }

        foreach (self::METHOD_LABELS as $needle => $label) {
            if (str_contains($text, $needle)) {
                return $label;
            }
        }

        return ucfirst($text);
    }

    private static function normalizeKey(mixed $key): string
    {
        return preg_replace('/[^a-z0-9]/', '', strtolower((string) $key)) ?? '';
    }
}
