<?php

/**
 * Class ProfileModel
 */
class ProfileModel extends Cf_Model
{
    public const USER_URL = '/user/%s';
    public const ME_URL = '/me';
    private $pdo;

    public const BUILD_APP_OPENED_KEY = 'build_app_opened';
    public const GROUPS_APP_OPENED_KEY = 'groups_app_opened';
    public const FIRST_lOGIN_AFTER_RESET_KEY = 'first_login_after_reset';
    public const IS_AI_AGENT_OPENED_KEY = 'is_ai_agent_opened';
    public const DISABLED_TWO_FA_DASHBOARD_NOTIFICATION_AT_KEY = 'disabled_two_fa_dashboard_notification_at';

    public const ALLOWED_KEYS = [
        self::BUILD_APP_OPENED_KEY,
        self::GROUPS_APP_OPENED_KEY,
        self::FIRST_lOGIN_AFTER_RESET_KEY,
        self::IS_AI_AGENT_OPENED_KEY,
        self::DISABLED_TWO_FA_DASHBOARD_NOTIFICATION_AT_KEY,
    ];

    private $cacheKeySuffix;
    private $cacheTTL;
    private $longCacheTTL;
    public function __construct()
    {
        $this->pdo = $this->db->db_connect();
        $this->load->driver('cache', ['adapter' => 'file']);
        $this->cacheTTL = $this->config->item('default_cache_ttl');
        $this->longCacheTTL = $this->config->item('long_cache_ttl');
    }

    /**
     * @param $username
     * @return array
     */
    public function getProfileData(string $username)
    {
        $profileData = json_decode(
            $this->getRestClient()->get(sprintf(self::USER_URL, $username)),
            JSON_OBJECT_AS_ARRAY,
        );

        $result = isset($profileData['data']) ? $profileData['data'][0] : [];

        $isVerificationNeeded = json_decode(
            $this->getRestClient()->get('/2fa/verification-needed'),
            JSON_OBJECT_AS_ARRAY,
        );

        if (isset($isVerificationNeeded['result'])) {
            $result['is2FaVerificationNeeded'] = $isVerificationNeeded['result'];
        }

        return $result;
    }

    /**
     * @return array
     */
    public function getTimezoneList()
    {
        //get time zones from postgres
        $timeZones = $this->db
            ->query('SELECT * FROM pg_timezone_names WHERE name LIKE \'%/%\' ORDER BY name')
            ->result_array();
        $result = [];
        foreach ($timeZones as $tz) {
            [$continent, $city] = explode('/', $tz['name']);

            $utcOffset = $this->convertUtcOffset($tz['utc_offset']);
            $result[$continent][] = [
                'label' => $city . " (GMT $utcOffset)",
                'value' => $tz['name'],
            ];
        }
        return $result;
    }

    /**
     * @param $name
     * @return array
     */
    public function getTimeZone($name)
    {
        $timeZone = $this->db->like('name', $name)->get('pg_timezone_names')->row_array();

        $utcOffset = $this->convertUtcOffset($timeZone['utc_offset']);
        $result = [
            'label' => $timeZone['name'] . " (GMT $utcOffset)",
            'value' => $timeZone['name'],
            'offset' => $utcOffset,
            'minutesOffset' => $this->convertOffsetToMinutes($utcOffset),
        ];

        return $result;
    }

    /**
     * convert offset from `01:00:00` to `+01:00` or `-02:00:00` to `-02:00`
     */
    private function convertUtcOffset($offset)
    {
        return $offset[0] == '-' ? substr($offset, 0, 6) : '+' . substr($offset, 0, 5);
    }

    private function convertOffsetToMinutes($offset)
    {
        $sign = $offset[0];
        [$hours, $minutes] = explode(':', substr($offset, 1));
        $offsetInMinutes = $sign . (intval($hours) * 60 + intval($minutes));
        return intval($offsetInMinutes);
    }

    /**
     * @param $data
     * @param $username
     * @return mixed
     */
    public function updateProfile($data, $username)
    {
        $this->getRestClient()->post(sprintf(self::USER_URL, $username), $data);
        $this->cache->delete($this->getTzCacheKey($username));
        return true;
    }

    /**
     * @param $data
     * @param $username
     * @return mixed
     */
    public function updateTimeZone($data, $username)
    {
        $this->getRestClient()->post(self::ME_URL, $data);
        $this->cache->delete($this->getTzCacheKey($username));
        return true;
    }

    /**
     * @param $value
     * @param $username
     */
    public function setNeverAskTimeZoneChange($value, $username)
    {
        $this->db->where('username', $username)->update('users', ['never_ask_timezone_change' => $value]);
    }

    /**
     * @param $value
     * @param $username
     */
    public function setUseBrowserTime($value, $username)
    {
        $this->db->where('username', $username)->update('users', ['use_browser_time' => $value]);
    }

    /**
     * @param $username
     * @return int
     */
    public function getNeverAskTimeZoneChange($username)
    {
        $value = $this->db
            ->where('username', $username)
            ->select('never_ask_timezone_change')
            ->get('users')
            ->row_array();
        return isset($value['never_ask_timezone_change']) ? $value['never_ask_timezone_change'] : 0;
    }

    public function getCurrentTimeZone($username)
    {
        // cache user's profile timezone as it calls the DB and is often used
        // cache will be invalidated when the user updates their timezone
        // otherwise, the cache invalidates automatically in 1 day
        $cacheKey = $this->getTzCacheKey($username);

        if ($this->cache->get($cacheKey)) {
            $tz = $this->cache->get($cacheKey);
        } else {
            $profile = $this->getProfileData($username);
            $tz = isset($profile['time_zone']) ? $this->getTimeZone($profile['time_zone']) : null;
            $this->cache->save($cacheKey, $tz, $this->longCacheTTL);
        }
        return $tz;
    }

    /**
     * @param $username
     * @return int
     */
    public function getUseBrowserTime($username)
    {
        $value = $this->db->where('username', $username)->select('use_browser_time')->get('users')->row_array();
        return isset($value['use_browser_time']) ? $value['use_browser_time'] : 0;
    }

    /**
     * @param $value
     * @param $username
     */
    public function setDarkMode(int $value, string $username)
    {
        $this->db->where('username', $username)->update('users', ['dark_mode' => $value]);
    }

    public function getDarkMode($username)
    {
        $value = $this->db->where('username', $username)->select('dark_mode')->get('users')->row_array();
        return isset($value['dark_mode']) ? $value['dark_mode'] : 0;
    }

    public function getAdditionalData(string $username, string $key)
    {
        $cacheKey = $this->getCacheKey($username . $key);

        if ($this->cache->get($cacheKey)) {
            $additionalData = $this->cache->get($cacheKey);
        } else {
            $stmt = $this->pdo->prepare('SELECT additional_data->:key as data FROM users WHERE username = :username');
            $stmt->execute(['key' => $key, 'username' => $username]);
            $additionalData = $stmt->fetch(PDO::FETCH_ASSOC)['data'];
            $this->cache->save($cacheKey, $additionalData, $this->cacheTTL);
        }

        return $additionalData;
    }

    public function setAdditionalData(string $username, string $key, $data)
    {
        $cacheKey = $this->getCacheKey($username . $key);
        $stmt = $this->pdo->prepare(
            'UPDATE users SET additional_data = jsonb_set(additional_data, :key, :data) WHERE username = :username',
        );
        $stmt->execute(['key' => '{' . $key . '}', 'data' => json_encode($data), 'username' => $username]);
        $this->cache->delete($cacheKey);
    }

    public function isKeyAllowed(string $key): bool
    {
        return in_array($key, self::ALLOWED_KEYS);
    }

    private function getCacheKey(string $username): string
    {
        return "{$username}_additonaldata";
    }

    private function getTzCacheKey(string $username): string
    {
        return "{$username}_timezone";
    }
}
