<?php

namespace App\Shared\Service;

use App\Shared\Constant\ErrorCodes;
use App\Shared\Exception\AppException;
use Psr\Log\LoggerInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

/**
 * کلاینت استعلام هویت api.ir (s.api.ir).
 *
 * دو سرویس: شاهکار (تطبیق کد ملی با موبایل) و IbanMatch (تطبیق شبا با کد ملی).
 *
 * نگاشت فیلدها بر اساس قرارداد متداول api.ir پیاده شده است. اگر پاسخ واقعی
 * سرویس نام فیلد متفاوتی داشت، فقط همین کلاس (متدهای parse*) باید اصلاح شود.
 *
 * رفتار «fail-closed»: اگر توکن پیکربندی نشده باشد، استعلام انجام نمی‌شود و
 * خطا برمی‌گردد — هرگز به‌صورت پیش‌فرض «تأییدشده» برنمی‌گرداند تا مسیر پولی
 * (مالکیت شبا) به‌اشتباه تأیید نشود.
 */
class ApiIrService
{
    public function __construct(
        private readonly HttpClientInterface $httpClient,
        private readonly LoggerInterface $logger,
        private readonly string $baseUrl,   // https://s.api.ir
        private readonly string $token,     // توکن api.ir — از .env
    ) {}

    public function isConfigured(): bool
    {
        return $this->token !== '';
    }

    /**
     * تطبیق کد ملی با موبایل (شاهکار). true یعنی هر دو متعلق به یک نفر است.
     */
    public function shahkarMatch(string $nationalCode, string $mobile): bool
    {
        // پاسخ: {"data": true|false, "success": true, "code": 0, "message": "..."}
        $data = $this->post('/api/sw1/ShahkarLite', [
            'nationalCode' => $nationalCode,
            'mobile'       => $mobile,
        ]);

        return $this->extractMatched($data);
    }

    /**
     * تطبیق شبا با کد ملی و تاریخ تولد.
     *
     * پاسخ سرویس فقط نتیجه‌ی boolean (`data`) می‌دهد و نام بانک/صاحب حساب را برنمی‌گرداند.
     *
     * @param string $birthDate تاریخ تولد شمسی به فرمت Y/m/d (مثلاً 1370/01/01)
     * @return array{matched: bool, bank_name: ?string, owner_name: ?string}
     */
    public function ibanMatch(string $iban, string $nationalCode, string $birthDate): array
    {
        $data = $this->post('/api/sw1/IbanMatch', [
            'iban'         => $iban,
            'nationalCode' => $nationalCode,
            'birthDate'    => $birthDate,
        ]);

        return [
            'matched'    => $this->extractMatched($data),
            'bank_name'  => null,
            'owner_name' => null,
        ];
    }

    /**
     * استعلام هویت شخص از روی کد ملی و تاریخ تولد (PersonInfo).
     *
     * @param string $birthDateJalali تاریخ تولد شمسی به فرمت Y/m/d (مثلاً 1371/1/1)
     * @return array{firstName: string, lastName: string, alive: bool}|null null یعنی رکوردی مطابقت نکرد.
     */
    public function personInfo(string $nationalCode, string $birthDateJalali): ?array
    {
        $data = $this->post('/api/sw1/PersonInfo', [
            'nationalCode' => $nationalCode,
            'birthDate'    => $birthDateJalali,
        ]);

        $person = $data['data'] ?? null;
        if (!is_array($person) || ($person['nationalCode'] ?? '') === '') {
            return null;
        }

        return [
            'firstName' => (string) ($person['firstName'] ?? ''),
            'lastName'  => (string) ($person['lastName'] ?? ''),
            'alive'     => filter_var($person['alive'] ?? false, FILTER_VALIDATE_BOOLEAN),
        ];
    }

    /**
     * @param array<string,mixed> $payload
     * @return array<string,mixed>
     */
    private function post(string $path, array $payload): array
    {
        if (!$this->isConfigured()) {
            throw new AppException(ErrorCodes::ERR_EXTERNAL_NOT_CONFIGURED, null, 503);
        }

        try {
            $response = $this->httpClient->request('POST', rtrim($this->baseUrl, '/') . $path, [
                'json'    => $payload,
                'headers' => ['Authorization' => 'Bearer ' . $this->token],
                'timeout' => 10,
            ]);

            $status = $response->getStatusCode();
            if ($status >= 500) {
                throw new AppException(ErrorCodes::ERR_EXTERNAL_001, null, 502);
            }

            return $response->toArray(false);
        } catch (AppException $e) {
            throw $e;
        } catch (\Throwable $e) {
            $this->logger->error(sprintf('api.ir inquiry failed: %s @ %s:%d', $e->getMessage(), $e->getFile(), $e->getLine()), ['exception' => $e, 'path' => $path]);
            throw new AppException(ErrorCodes::ERR_EXTERNAL_001, null, 502);
        }
    }

    /**
     * استخراج نتیجه‌ی boolean از پاسخ api.ir.
     *
     * قالب پاسخ: {"data": true|false, "success": true, "code": 0, "message": "..."}
     * - success=false یعنی ورودی نامعتبر/خطای درخواست → نتیجه «عدم تطبیق».
     * - data همان نتیجه‌ی boolean تطبیق است.
     *
     * @param array<string,mixed> $data
     */
    private function extractMatched(array $data): bool
    {
        if (($data['success'] ?? false) !== true) {
            return false;
        }

        return filter_var($data['data'] ?? false, FILTER_VALIDATE_BOOLEAN);
    }
}
