<?php
include_once '_utils.php';

define('FMF_UF_SCROLLBEYOND', 1);
//define('FMF_UF_NEWSTYLE', 2);

function get_user_id(string $username, string $email): int {
    global $pdo;

    $checkUser = $pdo->prepare('SELECT `user_id` FROM `fmf_users` WHERE LOWER(`user_login`) = LOWER(:login) OR LOWER(`user_email`) = LOWER(:email)');
    $checkUser->bindValue('login', $username);
    $checkUser->bindValue('email', $email);
    $checkUser->execute();

    return (int)$checkUser->fetchColumn();
}

function get_user_for_login(string $nameOrMail): array {
    global $pdo;

    $getUser = $pdo->prepare('SELECT `user_id`, `user_login`, `user_password`, `user_email_verification` FROM `fmf_users` WHERE LOWER(`user_login`) = LOWER(:login) OR LOWER(`user_email`) = LOWER(:email)');
    $getUser->bindValue('login', $nameOrMail);
    $getUser->bindValue('email', $nameOrMail);
    $user = $getUser->execute() ? $getUser->fetch(PDO::FETCH_ASSOC) : false;

    return $user ? $user : [];
}

function user_info(?int $user, bool $fresh = false): array {
    global $pdo;
    static $cache = [];

    if($user < 1)
        return [];
    if(!$fresh && !empty($cache[$user]))
        return $cache[$user];

    $getUserInfo = $pdo->prepare('SELECT *, UNIX_TIMESTAMP(`user_created`) AS `user_created`, UNIX_TIMESTAMP(`user_banned`) AS `user_banned`, MD5(LOWER(TRIM(`user_email`))) AS `gravatar_hash` FROM `fmf_users` WHERE `user_id` = :user');
    $getUserInfo->bindValue('user', $user);
    $userInfo = $getUserInfo->execute() ? $getUserInfo->fetch(PDO::FETCH_ASSOC) : false;

    return $cache[$user] = ($userInfo ? $userInfo : []);
}

function user_has_flag(int $user, int $flag, bool $strict = false): bool {
    $userInfo = user_info($user);

    if(empty($userInfo))
        return false;

    $flags = ($userInfo['user_flags'] & $flag);

    return $strict ? ($flags === $flag) : ($flags > 0);
}

function create_user(string $username, string $email, string $password, string $ipAddr, bool $verified = false): array {
    global $pdo;

    $verification = $verified ? null : bin2hex(random_bytes(16));

    $createUser = $pdo->prepare('INSERT INTO `fmf_users` (`user_login`, `user_password`, `user_email`, `user_email_verification`, `user_ip_created`) VALUES (:login, :password, :email, :verification, INET6_ATON(:ip))');
    $createUser->bindValue('login', $username);
    $createUser->bindValue('password', password_hash($password, PASSWORD_DEFAULT));
    $createUser->bindValue('email', $email);
    $createUser->bindValue('verification', $verification);
    $createUser->bindValue('ip', $ipAddr);
    $createUser->execute();

    return [
        'user_id' => (int)$pdo->lastInsertId(),
        'verification' => $verification,
    ];
}

function validate_username(string $username): ?string {
    if($username !== trim($username))
        return 'Your username may not start or end with spaces.';

    $usernameLength = strlen($username);

    if($usernameLength < 3)
        return 'Your username must be longer than 3 characters.';

    if($usernameLength > 16)
        return 'Your username may not be longer than 16 characters.';

    if(!preg_match('#^[A-Za-z0-9-_]+$#u', $username))
        return 'Your username may only contains alphanumeric characters, dashes and underscores (A-Z, a-z, 0-9, -, _).';

    return null;
}

function validate_email(string $email): ?string {
    if(filter_var($email, FILTER_VALIDATE_EMAIL) === false)
        return 'Your e-mail address is not correctly formatted.';

    $domain = mb_substr(mb_strstr($email, '@'), 1);

    if(!checkdnsrr($domain, 'MX') && !checkdnsrr($domain, 'A'))
        return 'Your e-mail address domain does not have valid DNS records.';

    return null;
}

function validate_password(string $password): ?string {
    if(unique_chars($password) < 10)
        return 'Your password is too weak.';

    return null;
}

function activate_user(string $code): void {
    global $pdo;

    if(strlen($code) !== 32)
        return;

    $verify = $pdo->prepare('UPDATE `fmf_users` SET `user_email_verification` = NULL WHERE `user_email_verification` = :code');
    $verify->bindValue('code', $code);
    $verify->execute();
}

function create_session(int $userId): string {
    global $pdo;

    if($userId < 1)
        return '';

    $sessionKey = bin2hex(random_bytes(32));

    $createSession = $pdo->prepare('INSERT INTO `fmf_sessions` (`user_id`, `sess_key`) VALUES (:user, :key)');
    $createSession->bindValue('user', $userId);
    $createSession->bindValue('key', $sessionKey);
    $createSession->execute();

    return $sessionKey;
}

function purge_old_sessions(): void {
    global $pdo;
    $pdo->exec('DELETE FROM `fmf_sessions` WHERE `sess_created` + INTERVAL 1 MONTH <= NOW()');
}

function session_activate(?string $key): void {
    global $pdo;

    if(empty($key) || strlen($key) !== 64)
        return;

    $verify = $pdo->prepare('SELECT `user_id` FROM `fmf_sessions` WHERE `sess_key` = :key AND `sess_created` + INTERVAL 1 MONTH > NOW()');
    $verify->bindValue('key', $key);
    $userId = $verify->execute() ? $verify->fetchColumn() : 0;

    if($userId < 1)
        return;

    $GLOBALS['fmf_user_id'] = (int)$userId;
}

function session_active(): bool {
    return !empty($GLOBALS['fmf_user_id']) && is_int($GLOBALS['fmf_user_id']) && $GLOBALS['fmf_user_id'] > 0;
}

function logout_token(): string {
    $sessionKey = $_COOKIE['fmfauth'] ?? '';

    if(strlen($sessionKey) !== 64 || !ctype_xdigit($sessionKey))
        return bin2hex(random_bytes(4));

    $offset = hexdec($sessionKey[0]) * 2;
    $offset = hexdec($sessionKey[$offset]) * 2;
    $offset = hexdec($sessionKey[$offset]) * 2;

    return substr($sessionKey, $offset, 8);
}

function destroy_session(string $token): void {
    global $pdo;

    $delete = $pdo->prepare('DELETE FROM `fmf_sessions` WHERE `sess_key` = :key');
    $delete->bindValue('key', $token);
    $delete->execute();
}

function destroy_user_sessions(int $userId): void {
    global $pdo;

    $delete = $pdo->prepare('DELETE FROM `fmf_sessions` WHERE `user_id` = :user');
    $delete->bindValue('user', $userId);
    $delete->execute();
}

function destroy_current_user_sessions(): void {
    destroy_user_sessions(current_user_id());
}

function current_user_id(): int {
    return session_active() ? $GLOBALS['fmf_user_id'] : 0;
}

function verify_password(string $pass, ?int $user = null): bool {
    global $pdo;
    $user = $user ?? current_user_id();

    if($user < 1)
        return false;

    $getHash = $pdo->prepare('SELECT `user_password` FROM `fmf_users` WHERE `user_id` = :user');
    $getHash->bindValue('user', $user);
    $hash = $getHash->execute() ? $getHash->fetchColumn() : '';

    if(empty($hash))
        return false;

    return password_verify($pass, $hash);
}

function user_set_password(int $user, string $password): void {
    global $pdo;

    if($user < 1)
        return;

    $password = password_hash($password, PASSWORD_DEFAULT);

    $update = $pdo->prepare('UPDATE `fmf_users` SET `user_password` = :pass WHERE `user_id` = :user');
    $update->bindValue('pass', $password);
    $update->bindValue('user', $user);
    $update->execute();
}

function user_set_email(int $user, string $email, bool $verified = false): ?string {
    global $pdo;

    if($user < 1)
        return null;

    $verification = $verified ? null : bin2hex(random_bytes(16));

    $update = $pdo->prepare('UPDATE `fmf_users` SET `user_email` = LOWER(:mail), `user_email_verification` = :verf WHERE `user_id` = :user');
    $update->bindValue('mail', $email);
    $update->bindValue('verf', $verification);
    $update->bindValue('user', $user);
    $update->execute();

    return $verification;
}

function user_gravatar(?int $user, int $res = 80): string {
    $authorInfo = user_info($user);
    return '//www.gravatar.com/avatar/'. ($authorInfo['gravatar_hash'] ?? str_repeat('0', 32)) .'?s='. $res .'&amp;r=g&amp;d=identicon';
}