<?php

declare(strict_types=1);

namespace app\common\library;

use app\common\model\MallItem;
use app\common\model\MallOrder;
use app\common\model\MallUserAsset;
use GuzzleHttp\Client;
use support\Log;

/**
 * 红利订单调用 PlayX bonus/grant（与定时任务、后台手动推送共用）
 */
final class MallBonusGrantPush
{
    /**
     * @return array{ok: bool, message: string}
     */
    public static function push(MallOrder $order): array
    {
        $conf = config('playx.angpow_import');
        if (!is_array($conf)) {
            return [
                'ok' => false,
                'message' => 'playX angpow_import not configured',
            ];
        }

        $baseUrl = rtrim(strval($conf['base_url'] ?? ''), '/');
        $path = strval($conf['path'] ?? '');
        $merchantCode = strval($conf['merchant_code'] ?? '');
        $authKey = strval($conf['auth_key'] ?? '');
        if ($baseUrl === '' || $path === '' || $merchantCode === '' || $authKey === '') {
            return [
                'ok' => false,
                'message' => 'playX Angpow Import API not configured',
            ];
        }

        $url = $baseUrl . $path;

        $asset = MallUserAsset::where('playx_user_id', $order->user_id)->find();
        if (!$asset || !is_string($asset->playx_user_id ?? null) || strval($asset->playx_user_id) === '') {
            return [
                'ok' => false,
                'message' => 'User asset not found',
            ];
        }

        $memberLogin = trim(strval($asset->username ?? ''));
        if ($memberLogin === '') {
            return [
                'ok' => false,
                'message' => 'User username empty',
            ];
        }

        $item = MallItem::where('id', $order->mall_item_id)->find();
        if (!$item) {
            return [
                'ok' => false,
                'message' => 'Item not found',
            ];
        }

        $reportDate = strval(time());
        $signatureInput = 'merchant_code=' . $merchantCode . '&report_date=' . $reportDate;
        $signature = self::buildSignature($signatureInput, $authKey);
        if ($signature === null) {
            return [
                'ok' => false,
                'message' => 'Build signature failed',
            ];
        }

        $start = gmdate('Y-m-d\TH:i:s\Z', strtotime(strval($order->start_time)));
        $end = gmdate('Y-m-d\TH:i:s\Z', strtotime(strval($order->end_time)));
        $multiplier = intval($order->multiplier ?? 0);
        if ($multiplier <= 0) {
            $multiplier = 1;
        }

        $payload = [
            'merchant_code' => $merchantCode,
            'report_date' => $reportDate,
            'angpow' => [
                [
                    'member_login' => $memberLogin,
                    'start_time' => $start,
                    'end_time' => $end,
                    'amount' => $order->amount,
                    'reward_name' => strval($item->title ?? ''),
                    'description' => strval($item->description ?? ''),
                    'member_inbox_message' => 'Congratulations! You received an angpow.',
                    'category' => strval($item->category ?? ''),
                    'category_title' => strval($item->category_title ?? ''),
                    'one_time_turnover' => 'yes',
                    'multiplier' => $multiplier,
                ],
            ],
            'currency_visual' => [
                [
                    'currency' => strval($conf['currency'] ?? 'MYR'),
                    'visual_name' => strval($conf['visual_name'] ?? 'Angpow'),
                ],
            ],
        ];

        $client = new Client([
            'timeout' => 20,
            'http_errors' => false,
        ]);

        try {
            $res = $client->post($url, [
                'headers' => [
                    'Content-Type' => 'application/json',
                    'X-Request-Signature' => $signature,
                ],
                'json' => $payload,
            ]);

            $httpStatus = 0;
            if (is_object($res) && method_exists($res, 'getStatusCode')) {
                $sc = $res->getStatusCode();
                if (is_int($sc)) {
                    $httpStatus = $sc;
                }
            }

            $rawBody = strval($res->getBody());
            $data = json_decode($rawBody, true);
            if (!is_array($data)) {
                $jsonDetail = 'root not JSON object';
                if (json_last_error() !== JSON_ERROR_NONE) {
                    $jem = json_last_error_msg();
                    $jsonDetail = is_string($jem) && $jem !== '' ? $jem : 'JSON parse error';
                }
                Log::error(
                    '[MallBonusGrantPush] response not a JSON object | order_id=' . $order->id
                    . ' | http=' . $httpStatus . ' | ' . $jsonDetail . ' | body=' . self::truncateForLog($rawBody)
                );

                return [
                    'ok' => false,
                    'message' => 'Invalid response',
                ];
            }

            if (($data['code'] ?? null) === '0' || ($data['code'] ?? null) === 0) {
                return [
                    'ok' => true,
                    'message' => '',
                ];
            }

            $remoteMsg = $data['message'] ?? $data['msg'] ?? '';
            if (!is_string($remoteMsg)) {
                $remoteMsg = '';
            }
            if ($remoteMsg === '') {
                $remoteMsg = 'playX angpow import not accepted';
            }

            $flags = JSON_UNESCAPED_UNICODE;
            if (defined('JSON_INVALID_UTF8_SUBSTITUTE')) {
                $flags = $flags | JSON_INVALID_UTF8_SUBSTITUTE;
            }
            $encoded = json_encode($data, $flags);
            if (!is_string($encoded)) {
                $encoded = $rawBody;
            }
            Log::error(
                '[MallBonusGrantPush] angpow import rejected | order_id=' . $order->id
                . ' | http=' . $httpStatus . ' | parsed=' . self::truncateForLog($encoded)
            );

            return [
                'ok' => false,
                'message' => $remoteMsg,
            ];
        } catch (\Throwable $e) {
            Log::error('[MallBonusGrantPush] request exception | order_id=' . $order->id . ' | ' . $e->getMessage());

            return [
                'ok' => false,
                'message' => $e->getMessage(),
            ];
        }
    }

    private static function truncateForLog(string $text, int $maxLen = 8000): string
    {
        $s = trim($text);
        if ($s === '') {
            return '';
        }
        $oneLine = preg_replace('/\s+/', ' ', $s);
        $snippet = is_string($oneLine) && $oneLine !== '' ? $oneLine : $s;
        if (function_exists('mb_strlen') && function_exists('mb_substr')) {
            if (mb_strlen($snippet, 'UTF-8') > $maxLen) {
                return mb_substr($snippet, 0, $maxLen, 'UTF-8') . '…';
            }

            return $snippet;
        }
        if (strlen($snippet) > $maxLen) {
            return substr($snippet, 0, $maxLen) . '…';
        }

        return $snippet;
    }

    private static function buildSignature(string $input, string $authKey): ?string
    {
        $keyBytes = null;

        $maybeBase64 = base64_decode($authKey, true);
        if ($maybeBase64 !== false && $maybeBase64 !== '') {
            $keyBytes = $maybeBase64;
        }

        if ($keyBytes === null) {
            $isHex = ctype_xdigit($authKey) && (strlen($authKey) % 2 === 0);
            if ($isHex) {
                $hex = hex2bin($authKey);
                if ($hex !== false && $hex !== '') {
                    $keyBytes = $hex;
                }
            }
        }

        if ($keyBytes === null) {
            $keyBytes = $authKey;
        }

        $raw = hash_hmac('sha1', $input, $keyBytes, true);
        if (!is_string($raw) || $raw === '') {
            return null;
        }

        return base64_encode($raw);
    }
}
