<?php

require_once __DIR__ . '/../../lib/Pest.php';
require_once __DIR__ . '/../../services/CFAuthService.php';

class OauthCfPdo extends OAuth2\Storage\Pdo
{
    public $username = null;

    public function getUser($username)
    {

        $userInfo = array();
        // the default behavior is to use "username" as the user_id
        return array_merge(array(
            'user_id' => $username
        ), $userInfo);
    }

    public function checkUserCredentials($username, $password)
    {
        $cfAuthService = new CFAuthService();
        return $this->username = $cfAuthService->auth($username, $password);
    }

    public function getUserDetails($username)
    {
        return $this->getUser($this->username);
    }

    /**
     * @param $access_tokens
     * @param $userId
     * @return bool
     *
     * If the userId is passed then will revoke only that user's tokens
     */
    public function unsetAccessToken($access_tokens, string|null $userId = null): true
    {
        if (empty($access_tokens)) {
            return true;
        }

        $place_holders = implode(',', array_fill(0, count($access_tokens), '?'));

        $executeData = $access_tokens;

        $userFilter = '';
        if ($userId !== null) {
            $userFilter = ' AND user_id = ? ';
            $executeData[] = $userId;
        }

        $stmt = $this->db->prepare(sprintf(
            'DELETE FROM %s WHERE access_token IN (%s) %s',
            $this->config['access_token_table'],
            $place_holders,
            $userFilter
        ));

        return $stmt->execute($executeData);
    }

    public function getActiveTokens()
    {
        $stmt = $this->db->prepare(sprintf("select json_agg(a) as details, a.user_id,count(a.access_token) as total_tokens from oauth_access_tokens a where expires > now() AT TIME ZONE 'UTC' GROUP BY user_id"));
        $stmt->execute();
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function getActiveTokensByUserId(string $userId)
    {
        $stmt = $this->db->prepare(
            "SELECT json_agg(a) AS details, a.user_id,count(a.access_token) AS total_tokens
             FROM oauth_access_tokens a
             WHERE user_id = :user_id
              AND expires > now() AT TIME ZONE 'UTC'
             GROUP BY user_id"
        );
        $stmt->execute(['user_id' => $userId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    public function removeExternalUsersTokens()
    {
        return $this->db
            ->query(sprintf(
                'DELETE FROM %s WHERE user_id IN (SELECT username FROM users WHERE external = TRUE)',
                $this->config['access_token_table']
            ))
            ->execute();
    }

    public function setToken2FaVerified($accessToken): bool
    {
        $statement = $this->db->prepare('UPDATE oauth_access_tokens SET two_factor_verified_at = now() WHERE access_token = :access_token');
        $statement->bindParam(':access_token', $accessToken, PDO::PARAM_STR);
        return $statement->execute();
    }

    public function getToken2FaVerified($accessToken): ?string
    {
        $statement = $this->db->prepare('SELECT two_factor_verified_at FROM oauth_access_tokens WHERE access_token = :access_token');
        $statement->bindParam(':access_token', $accessToken, PDO::PARAM_STR);
        $statement->execute();
        return $statement->fetch(PDO::FETCH_ASSOC)['two_factor_verified_at'];
    }

    /**
     * @param $username
     * @param string|null $exceptToken
     * @return bool
     *
     * Invalidates all tokens except provided one
     */
    public function invalidateAllTokensExceptProvided($username, ?string $excludedToken = null): bool
    {
        $sql = 'DELETE FROM oauth_access_tokens WHERE user_id = :user_id';
        $bindData = ['user_id' => $username];

        if ($excludedToken !== null) {
            $sql .= ' AND access_token != :excludedToken ';
            $bindData['excludedToken'] = $excludedToken;
        }
        $stmt = $this->db->prepare($sql);
        return $stmt->execute($bindData);
    }
}
