<?php

namespace TechSoft\Laravel\Api;


use Carbon\Carbon;
use EdwinFound\Laravel\Cache\RedisUtil;
use EdwinFound\Laravel\Dao\ModelUtil;
use EdwinFound\Laravel\Http\Request;
use Illuminate\Support\Str;

class ApiSessionUtil
{
    const DATA_MAX_LENGTH = 2000;
    const EXPIRE_SECONDS = 2592000;
    const TOKEN_LENGTH = 64;

    const TYPE_NONE = 'none';
    const TYPE_DB = 'db';
    const TYPE_REDIS = 'redis';

    static $type = 'none';

    public static function setType($type)
    {
        self::$type = $type;
    }

    private static $tokenDataCache = [];

    public static function getToken()
    {
        return Request::headerGet('api-token', null);
    }

    public static function getOrGenerateToken()
    {
        $token = Request::headerGet('api-token', null);
        if (empty($token)) {
            $token = Str::random(self::TOKEN_LENGTH);
            Request::headerSet('api-token', $token);
        }
        return $token;
    }

    public static function all($token = null, $default = [])
    {
        if (empty($token)) {
            $token = Request::headerGet('api-token');
        }
        if (empty($token)) {
            return $default;
        }
        if (!isset(self::$tokenDataCache[$token])) {
            switch (self::$type) {
                case self::TYPE_NONE:
                    return $default;
                    break;
                case self::TYPE_REDIS:
                    $key = 'api-token:' . $token;
                    $data = RedisUtil::getObject($key);
                    if (empty($data)) {
                        $data = $default;
                    }
                    self::$tokenDataCache[$token] = $data;
                    RedisUtil::expire($key, self::EXPIRE_SECONDS);
                    break;
                case self::TYPE_DB:
                    $m = ModelUtil::get('api_token', ['token' => $token]);
                    if (empty($m)) {
                        self::$tokenDataCache[$token] = [];
                        return $default;
                    }
                    if (strtotime($m['expireTime']) < time()) {
                        ModelUtil::delete('api_token', ['token' => $token]);
                        self::$tokenDataCache[$token] = [];
                        return $default;
                    } else {
                        $data = @json_decode($m['data'], true);
                        if (empty($data)) {
                            $data = $default;
                        }
                        self::$tokenDataCache[$token] = $data;
                        $update = [];
                        $update['expireTime'] = Carbon::now()->addSeconds(self::EXPIRE_SECONDS);
                        ModelUtil::update('api_token', ['id' => $m['id']], $update);
                    }
            }
        }
        return self::$tokenDataCache[$token];
    }

    public static function get($name, $defaultValue = null, $token = null)
    {
        $all = self::all($token);
        if (isset($all[$name])) {
            return $all[$name];
        }
        return $defaultValue;
    }

    public static function put($name, $value, $token = null)
    {
        if (empty($token)) {
            $token = self::getOrGenerateToken();
        }

        switch (self::$type) {
            case self::TYPE_NONE:
                return true;
            case self::TYPE_REDIS:
                $key = 'api_token:' . $token;
                $m = RedisUtil::getObject($key);
                if (empty($m)) {
                    RedisUtil::setexObject($key, [], self::EXPIRE_SECONDS);
                    $m = [];
                }
                if (!isset(self::$tokenDataCache[$token])) {
                    self::$tokenDataCache[$token] = $m;
                }
                self::$tokenDataCache[$token][$name] = $value;
                if (null === $value) {
                    unset(self::$tokenDataCache[$token][$name]);
                }
                if (empty(self::$tokenDataCache[$token])) {
                    RedisUtil::delete($key);
                }
                RedisUtil::setexObject($key, self::$tokenDataCache[$token], self::EXPIRE_SECONDS);
                return true;
            case self::TYPE_DB:
                $m = ModelUtil::get('api_token', ['token' => $token]);
                if (empty($m)) {
                    $m = ModelUtil::insert('api_token', ['token' => $token, 'data' => json_encode([])]);
                }

                if (!isset(self::$tokenDataCache[$token])) {
                    self::$tokenDataCache[$token] = @json_decode($m['data'], true);
                }
                self::$tokenDataCache[$token][$name] = $value;
                if (null === $value) {
                    unset(self::$tokenDataCache[$token][$name]);
                }
                if (empty(self::$tokenDataCache[$token])) {
                    ModelUtil::delete('api_token', ['id' => $m['id']]);
                    return true;
                }
                $dataJson = json_encode(self::$tokenDataCache[$token]);
                if (strlen($dataJson) > self::DATA_MAX_LENGTH) {
                    throw new \Exception('ApiSessionService.LengthOversize -> ' . $dataJson);
                    return false;
                }
                $update = [];
                $update['data'] = $dataJson;
                $update['expireTime'] = Carbon::now()->addSeconds(self::EXPIRE_SECONDS);
                ModelUtil::update('api_token', ['id' => $m['id']], $update);
                return true;
        }
        return false;
    }

    public static function forget($name, $token = null)
    {
        if (empty($token)) {
            $token = Request::headerGet('api-token');
        }
        if (empty($token)) {
            return true;
        }
        self::put($name, null, $token);
    }
}
