<?php

namespace Shm\ShmBlueprints\Auth;

use Shm\Shm;
use Shm\ShmAuth\Auth;
use Shm\ShmDB\mDB;
use Shm\ShmUtils\I18n;
use Shm\ShmUtils\Response;

class ShmSmsSet extends ShmAuthBase
{
    private string $smsSetAuthCollection = '_sms_set_auth';

    private array $testPhones = [
        79201111111,
        79202222222,
    ];

    private mixed $smsSendFunctionHandler = null;
    private mixed $smsCodeFunctionHandler = null;

    private int $timeLiveSmsSetCode = 60 * 5;

    public function setSmsSendFunctionHandler(callable $handler): static
    {
        $this->smsSendFunctionHandler = $handler;
        return $this;
    }

    public function setSmsCodeFunctionHandler(callable $handler): static
    {
        $this->smsCodeFunctionHandler = $handler;
        return $this;
    }

    private function getCode($phone)
    {
        if ($this->smsCodeFunctionHandler) {
            return call_user_func($this->smsCodeFunctionHandler, $phone);
        }

        return \random_int(1111, 9999);
    }

    private function sendSms($phone, $code)
    {
        if ($this->smsSendFunctionHandler) {
            return call_user_func($this->smsSendFunctionHandler, $phone, $code);
        }

        return false;
    }

    public function make(): array
    {
        if (count($this->_authStructures) === 0) {
            throw new \Exception("No auth structures defined for Sms Set");
        }

        return [
            'type' => Shm::boolean(),
            'description' => 'Установка телефона через SMS с подтверждением',
            'args' => Shm::structure([
                'phone' => Shm::nonNull(Shm::string()),
                'code' => Shm::int(),
            ]),
            'resolve' => function ($root, $args) {
                $phone = (int) preg_replace("/[^,.0-9]/", '', $args['phone']);

                if (isset($args['code'])) {
                    return $this->confirmPhone($phone, (int) $args['code']);
                }

                return $this->sendConfirmationCode($phone);
            },
        ];
    }

    private function sendConfirmationCode(int $phone)
    {
        $this->validatePhoneSetRequest($phone);

        $collection = mDB::collection($this->smsSetAuthCollection);

        if (in_array($phone, $this->testPhones)) {
            $code = 9876;

            $collection->insertOne([
                'user_id' => mDB::id(Auth::getAuthID()),
                'auth_collection' => Auth::getAuthCollection(),
                'phone' => $phone,
                'code' => $code,
                'created_at' => time(),
                'ip' => $_SERVER['REMOTE_ADDR'] ?? null,
            ]);

            return null;
        }

        $findExisting = $collection->findOne(
            [
                'user_id' => mDB::id(Auth::getAuthID()),
                'phone' => $phone,
                'created_at' => ['$gt' => time() - $this->timeLiveSmsSetCode],
            ],
            [
                'sort' => ['_id' => -1],
            ]
        );

        if ($findExisting) {
            $code = (int) $findExisting->code;

            $collection->insertOne([
                'reSend' => true,
                'repeat_from' => $findExisting->created_at,
                'user_id' => mDB::id(Auth::getAuthID()),
                'auth_collection' => Auth::getAuthCollection(),
                'phone' => $phone,
                'code' => $code,
                'created_at' => time(),
                'ip' => $_SERVER['REMOTE_ADDR'] ?? null,
            ]);
        } else {
            $code = $this->getCode($phone);

            $collection->insertOne([
                'user_id' => mDB::id(Auth::getAuthID()),
                'auth_collection' => Auth::getAuthCollection(),
                'phone' => $phone,
                'code' => (int) $code,
                'created_at' => time(),
                'ip' => $_SERVER['REMOTE_ADDR'] ?? null,
            ]);
        }

        if (in_array($phone, $this->testPhones)) {
            return null;
        }

        $this->sendSms($phone, $code);

        return null;
    }

    private function confirmPhone(int $phone, int $code): bool
    {
        [$phoneField, $currentAuthStructure] = $this->validatePhoneSetRequest($phone);

        $findSmsSet = mDB::collection($this->smsSetAuthCollection)->findOne(
            [
                'user_id' => mDB::id(Auth::getAuthID()),
                'phone' => $phone,
                'code' => $code,
                'created_at' => ['$gt' => time() - $this->timeLiveSmsSetCode],
            ],
            [
                'sort' => ['_id' => -1],
            ]
        );

        if (!$findSmsSet) {
            Response::validation(I18n::get('wrong_sms_code'));
        }

        return $this->setPhoneForCurrentUser($phone, $phoneField, $currentAuthStructure);
    }
}
