<?php

namespace Shm\ShmPayment\TBankPayment;



use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
use Shm\Shm;
use Shm\ShmCmd\Cmd;
use Shm\ShmDB\mDB;
use Shm\ShmPayment\TBankPayment\TBankCollections\TCustomerCards;
use Shm\ShmPayment\TBankPayment\TBankCollections\TPayments;
use Shm\ShmTypes\StructureType;
use Shm\ShmUtils\ShmUtils;

class TBankPayment
{


    const API_ENDPOINT = 'https://securepay.tinkoff.ru/v2/';

    const WEBHOOK_URL = '/webhook/tbank/payment';

    protected  $default_http_client_options = [
        'connect_timeout' => 4,
        'timeout' => 10
    ];

    public static $terminalKey = null;
    public static $terminalPassword =  null;

    public static $successURL = null;
    public static $failURL = null;



    public $customerID = null;
    public $customerPhone = null;
    public $customerStructure = null;


    public static function setSuccessURL($successURL)
    {
        self::$successURL = $successURL;
    }
    public static function setFailURL($failURL)
    {
        self::$failURL = $failURL;
    }


    public static function setTerminalKey($terminalKey)
    {
        self::$terminalKey = $terminalKey;
    }
    public static function setTerminalPassword($password)
    {
        self::$terminalPassword = $password;
    }

    public function __construct(StructureType $customerStructure, $customer_id)
    {


        if (!self::$terminalKey || !self::$terminalPassword) {
            throw new \Exception("Terminal key or password is not set");
        }


        $this->customerStructure = $customerStructure;

        $phoneField = $customerStructure->findItemByType(Shm::phone());
        if (!$phoneField) {
            throw new \Exception("Phone field not found");
        }

        $customer = $customerStructure->findOne([
            "_id" => mDB::id($customer_id),
            $phoneField->key => [
                '$exists' => true,
                '$ne' => null,
            ]
        ]);

        if (!$customer) {
            throw new \Exception("Customer not found");
        }

        $this->customerID =  $customer['_id'];
        $this->customerPhone = '+' . $customer[$phoneField->key];
    }


    public function getCustomerKey()
    {
        return md5($this->customerID) . md5($this->customerPhone) . md5(self::$terminalKey);
    }


    public function syncCustomerCards()
    {
        $response = $this->sendRequest('GetCardList', [
            "CustomerKey" => $this->getCustomerKey(),
        ]);


        $cards = $response ?? [];

        foreach ($cards as $card) {

            TCustomerCards::updateOne(
                [
                    'CardId' => $card['CardId'],
                    'deleted_at' => ['$exists' => false],
                    "owner" => mDB::id($this->customerID),
                    "ownerCollection" => $this->customerStructure->collection,
                    'terminalKey' => self::$terminalKey,

                ],
                [

                    '$set' => [
                        ...$card,
                        'terminalKey' => self::$terminalKey,
                        "owner" => mDB::id($this->customerID),
                        "ownerCollection" => $this->customerStructure->collection,
                    ]

                ],
                [
                    'upsert' => true,
                ]
            );
        }
    }


    public function Cancel($PaymentId)
    {
        $response = $this->sendRequest('Cancel', [
            "PaymentId" => $PaymentId,
        ]);

        return $response;
    }

    public function Charge($PaymentId, $RebillId)
    {
        $response = $this->sendRequest('Charge', [
            "PaymentId" => $PaymentId,
            "RebillId" => $RebillId,
        ]);
        return $response;
    }


    public  function initPayment($sum, $description)
    {

        $orderId = uniqid() . '' . uniqid() . 'order';

        $data = [
            'Amount' => $sum * 100,
            'OrderId' => $orderId,
            'Description' => $description,
            'DATA' => [
                'Phone' => $this->customerPhone,
            ],
            'Recurrent' => 'Y',
            'CustomerKey' => $this->getCustomerKey(),

            'NotificationURL' => ShmUtils::url(self::WEBHOOK_URL),
        ];

        if (self::$successURL) {
            $data['SuccessURL'] = self::$successURL;
        }
        if (self::$failURL) {
            $data['FailURL'] = self::$failURL;
        }


        // 'SuccessURL' => self::$successURL,
        //  'FailURL' => self::$failURL,


        /*  $data['Receipt'] = [
            'Taxation' => 'usn_income',
            'Items' => [
                [
                    'Name' => $data['Description'],
                    'Price' =>  $data['Amount'],
                    'Quantity' => 1,
                    'Amount' =>  $data['Amount'],
                    'Tax' => 'none',
                    'PaymentObject' => 'service',
                ]
            ]
        ];*/

        $payment = TPayments::insertOne([
            "amount" => $sum,
            "orderId" => $orderId,
            "description" => $description,
            "phone" => $this->customerPhone,
            "owner" => mDB::id($this->customerID),
            "ownerCollection" => $this->customerStructure->collection,
            'initRequest' => $data,
        ]);


        $response = $this->sendRequest('Init', $data);

        TPayments::updateOne(
            [
                '_id' => $payment->getInsertedId()
            ],
            [
                '$set' => ['initResult' => $response]
            ]
        );

        return $response;
    }

    public function removeCard($CardId)
    {

        $response = $this->sendRequest('RemoveCard', [
            "CardId" => $CardId,
            "CustomerKey" => $this->getCustomerKey(),
        ]);

        return $response;
    }

    public function addCard()
    {



        $response = $this->sendRequest('Init', [
            'Amount' => 100,
            'Recurrent' => 'Y',
            "CustomerKey" => $this->getCustomerKey(),
            'OrderId' => uniqid() . '' . uniqid(),
            'Description' => 'Оплата услуг сервиса ЯКЛЕНЕР',
            'SuccessURL' => self::$successURL,
            'FailURL' => self::$failURL,
            'NotificationURL' => ShmUtils::url(self::WEBHOOK_URL),
            'DATA' => [
                'Phone' => '+' . $this->customerPhone,
            ]

        ]);

        if ($response['Success']) {
            return $response['PaymentURL'];
        } else {
            Shm::error("Ошибка при добавлении карты");
        }
    }

    protected  function generateToken(array $params)
    {
        foreach (['SHOPS', 'RECEIPT', 'DATA'] as $ignore_key) {
            if (isset($params[$ignore_key])) {
                unset($params[$ignore_key]);
            }
        }

        $params['Password'] = self::$terminalPassword;
        ksort($params);

        $token = '';
        foreach ($params as $param_value) {
            if (is_scalar($param_value)) {
                $token .= $param_value;
            }
        }

        return hash('sha256', $token);
    }

    public  function sendRequest($method, $data)
    {


        $data['TerminalKey'] = self::$terminalKey;


        $data['Token'] = $this->generateToken($data);
        $url = self::API_ENDPOINT . $method;

        $headers = [
            'Content-Type' => 'application/json',
        ];


        $client = new Client($this->default_http_client_options);


        $request = new Request('POST', $url, $headers, json_encode($data));
        $response = $client->sendAsync($request)->wait();


        $body = json_decode($response->getBody(), true);



        return $body;
    }


    public static function webhookHandler()
    {

        $f3 = \Base::instance();
        $f3->route('POST|GET ' . self::WEBHOOK_URL, function ($f3) {

            $data = ShmUtils::allRequest();

            $OrderId = $data['OrderId'] ?? null;

            if ($OrderId) {

                $payment = TPayments::findOne([
                    'orderId' => $OrderId
                ]);

                if ($payment) {

                    $notifications = $payment->notifications ?? [];
                    $notifications = (array) $notifications;
                    $notifications[] = $data;


                    TPayments::updateOne([
                        '_id' => $payment['_id']
                    ], [
                        '$set' => ['notifications' => $notifications]
                    ]);
                }
            }
        });
    }
}