<?php

namespace Velis\Api;

use DateTimeZone;
use Exception;
use ReflectionException;
use User\User;
use Velis\Arrays;
use Velis\Model\DataObject;
use Velis\Model\Sanitizable;
use Velis\Output;

/**
 * API token model
 * @author Michał Nosek <michal.nosek@velis.pl>
 */
class Token extends DataObject implements Sanitizable
{
    /**
     * User object
     * @var \Velis\User
     */
    protected $_user;


    /**
     * Returns related sql table
     * @return string
     */
    protected function _getTableName()
    {
        return 'acl.user_token_tab';
    }


    /**
     * Return token id
     * @return string
     */
    public function getName()
    {
        return $this->id();
    }


    /**
     * Creates new token
     * @param \Velis\User $user
     * @param string|null $refreshToken
     * @return static
     * @throws DataObject\NoColumnsException
     * @throws Exception
     */
    public static function create(\Velis\User $user, $refreshToken = null)
    {
        $token = new self([
            'user_token_id' => $refreshToken ?: md5(microtime() . $user->id() . 'ApIResT'),
            'refresh_hash' => md5(microtime() . 'NoFresh'),
            'user_id' => $user->id(),
        ]);

        $token->_user = $user;

        return $token->add()->load();
    }


    /**
     * Returns token owner
     * @return \Velis\User
     */
    public function getUser()
    {
        if (!isset($this->_user)) {
            $this->_user = User::bufferedInstance($this->user_id);
        }
        return $this->_user;
    }


    /**
     * Removes token
     */
    public function remove()
    {
        return parent::_remove();
    }


    /**
     * Refresh token using refresh hash
     * @param array $data
     * @return Token|bool
     * @throws ReflectionException
     * @throws DataObject\NoColumnsException
     */
    public static function refreshToken($data)
    {
        $params = array_filter([
            'refresh_hash' => $data['refresh-token'],
            'user_id' => $data['user_id'],
        ]);

        if (count($params) !== 2) {
            $params = array_filter([
                'refresh_hash' => $data['refreshToken'],
                'user_id' => $data['userId'],
            ]);
        }

        if (count($params) === 2) {
            $token = Arrays::getFirst(
                self::listAll($params)
            );

            if ($token) {
                $newToken = self::create($token->getUser(), $token->refresh_hash);
                $token->remove();

                return $newToken;
            }
        }

        return false;
    }


    /**
     * Invalidates user tokens
     * @param array $userIds
     */
    public static function invalidateToken(array $userIds)
    {
        $commit = self::$_db->startTrans();

        try {
            $sql = "DELETE FROM acl.user_token_tab WHERE user_id IN (:user_ids)";
            $bindParams['user_ids'] = implode(',', $userIds);
            self::$_db->execDML($sql, $bindParams);

            if ($commit) {
                self::$_db->commit();
            }
        } catch (Exception $e) {
            if ($commit) {
                self::$_db->rollback();
            }
        }
    }


    /**
     * {@inheritDoc}
     * @throws Exception
     */
    public function getApiData()
    {
        $apiData = parent::getApiData();

        $timezone = $this->getUser()->getDateTimeZone();

        $apiData['date_created'] = Output::formatIsoDateTime($apiData['date_created'], $timezone);
        $apiData['date_used'] = Output::formatIsoDateTime($apiData['date_used'], $timezone);

        return $apiData;
    }
}
