<?php
declare(strict_types=1);

namespace Mcp;

use Exception;
use PDO;

class OpenSim
{

    private PDO $pdo;
    private $cache = array();

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    private function getUserNameFromGridData($userID, $table, $row): ?string
    {
        $statementGridUser = $this->pdo->prepare('SELECT '.$row.' FROM '.$table.' WHERE '.$row.' LIKE ?');
        $statementGridUser->execute(array($userID.';%'));

        while ($rowGridUser = $statementGridUser->fetch()) {
            $userData = explode(";", $rowGridUser[$row]);

            if (count($userData) >= 3) {
                $dbUserID = $userData[0];
                $dbUserName = $userData[2];

                $this->cache['USERNAME'][$userID] = $dbUserName;

                if ($dbUserID == $userID) {
                    return $dbUserName;
                }
            }
        }

        return null;
    }

    public function getUserName($userID): string
    {
        if ($userID == "00000000-0000-0000-0000-000000000000") {
            return "Unknown User";
        }

        if (isset($this->cache['USERNAME'][$userID])) {
            return $this->cache['USERNAME'][$userID];
        }

        $statementUser = $this->pdo->prepare('SELECT FirstName,LastName FROM UserAccounts WHERE PrincipalID = ?');
        $statementUser->execute(array($userID));

        if ($rowUser = $statementUser->fetch()) {
            $this->cache['USERNAME'][$userID] = $rowUser['FirstName'].' '.$rowUser['LastName'];
            return $this->cache['USERNAME'][$userID];
        }

        $res = $this->getUserNameFromGridData($this->pdo, $userID, 'GridUser', 'UserID');
        if ($res == null) {
            $res = $this->getUserNameFromGridData($this->pdo, $userID, 'Friends', 'PrincipalID');
        }

        return $res == null ? "Unknown User" : $res;
    }

    public function getUserUUID($userName): ?string
    {
        $statementUser = $this->pdo->prepare('SELECT PrincipalID,FirstName,LastName FROM UserAccounts WHERE FirstName = ? AND LastName = ?');
        $statementUser->execute(explode(' ', $userName));

        if ($rowUser = $statementUser->fetch()) {
            return $rowUser['PrincipalID'];
        }

        return null;
    }

    public function getRegionName($regionID): string
    {
        $statementRegion = $this->pdo->prepare("SELECT regionName FROM regions WHERE uuid = ?");
        $statementRegion->execute(array($regionID));

        if ($rowRegion = $statementRegion->fetch()) {
            return $rowRegion['regionName'];
        }

        return "Unknown Region";
    }

    public function getPartner($userID): string
    {
        $statement = $this->pdo->prepare("SELECT profilePartner FROM userprofile WHERE useruuid = ?");
        $statement->execute(array($userID));

        while ($row = $statement->fetch()) {
            if ($row['profilePartner'] != "00000000-0000-0000-0000-000000000000") {
                return $row['profilePartner'];
            }
        }

        return '';
    }

    public function allowOfflineIM($userID): string
    {
        $statement = $this->pdo->prepare("SELECT imviaemail FROM usersettings WHERE useruuid = ?");
        $statement->execute(array($userID));

        if ($row = $statement->fetch()) {
            return strtoupper($row['imviaemail']);
        }

        return "FALSE";
    }

    public function getUserMail($userID): string
    {
        $statement = $this->pdo->prepare("SELECT Email FROM UserAccounts WHERE PrincipalID = ?");
        $statement->execute(array($userID));

        if ($row = $statement->fetch()) {
            return $row['Email'];
        }

        return "";
    }

    private function getEntryCount($table): int
    {
        $statementCount = $this->pdo->prepare('SELECT COUNT(*) AS Count FROM '.$table);
        $statementCount->execute();
        if ($row = $statementCount->fetch()) {
            return $row['Count'];
        }
        return 0;
    }

    public function getUserCount(): int
    {
        return $this->getEntryCount('UserAccounts');
    }

    public function getRegionCount(): int
    {
        return $this->getEntryCount('regions');
    }

    public function getOnlineCount(): int
    {
        return $this->getEntryCount('Presence');
    }

    public function deleteUser($uuid): bool
    {
        try {
            $this->pdo->beginTransaction();

            $statementAuth = $this->pdo->prepare('DELETE FROM auth WHERE UUID = ?');
            $statementAuth->execute([$uuid]);

            $statementAgentPrefs = $this->pdo->prepare('DELETE FROM AgentPrefs WHERE PrincipalID = ?');
            $statementAgentPrefs->execute([$uuid]);

            $statementAvatars = $this->pdo->prepare('DELETE FROM Avatars WHERE PrincipalID = ?');
            $statementAvatars->execute([$uuid]);

            $statementGridUser = $this->pdo->prepare('DELETE FROM GridUser WHERE UserID = ?');
            $statementGridUser->execute([$uuid]);

            $statementEstateUser = $this->pdo->prepare('DELETE FROM estate_users WHERE uuid = ?');
            $statementEstateUser->execute([$uuid]);

            $statementEstateBan = $this->pdo->prepare('DELETE FROM estateban WHERE bannedUUID = ?');
            $statementEstateBan->execute([$uuid]);

            $statementHgTraveling = $this->pdo->prepare('DELETE FROM hg_traveling_data WHERE UserID = ?');
            $statementHgTraveling->execute([$uuid]);

            $statementUserIdentitys = $this->pdo->prepare('DELETE FROM UserIdentitys WHERE PrincipalID = ?');
            $statementUserIdentitys->execute([$uuid]);

            $statementFriends = $this->pdo->prepare('DELETE FROM Friends WHERE PrincipalID = ? OR Friend = ?');
            $statementFriends->execute([$uuid, $uuid]);

            $statementImOffline = $this->pdo->prepare('DELETE FROM im_offline WHERE PrincipalID = ?');
            $statementImOffline->execute([$uuid]);

            $statementInventoryFolders = $this->pdo->prepare('DELETE FROM inventoryfolders WHERE agentID = ?');
            $statementInventoryFolders->execute([$uuid]);

            $statementInventoryItems = $this->pdo->prepare('DELETE FROM inventoryitems WHERE avatarID = ?');
            $statementInventoryItems->execute([$uuid]);

            $statementGroupMembership = $this->pdo->prepare('DELETE FROM os_groups_membership WHERE PrincipalID = ?');
            $statementGroupMembership->execute([$uuid]);

            $statementGroupRoles = $this->pdo->prepare('DELETE FROM os_groups_rolemembership WHERE PrincipalID = ?');
            $statementGroupRoles->execute([$uuid]);

            $statementGroupRoles = $this->pdo->prepare('DELETE FROM Presence WHERE UserID = ?');
            $statementGroupRoles->execute([$uuid]);

            $statementMute = $this->pdo->prepare('DELETE FROM MuteList WHERE AgentID = ? OR MuteID = ?');
            $statementMute->execute([$uuid, $uuid]);

            $statementUserAccounts = $this->pdo->prepare('DELETE FROM UserAccounts WHERE PrincipalID = ?');
            $statementUserAccounts->execute([$uuid]);

            $statementUserData = $this->pdo->prepare('DELETE FROM userdata WHERE UserId = ?');
            $statementUserData->execute([$uuid]);

            $statementUserNotes = $this->pdo->prepare('DELETE FROM usernotes WHERE targetuuid = ?');
            $statementUserNotes->execute([$uuid]);

            $statementUserProfile = $this->pdo->prepare('DELETE FROM userprofile WHERE useruuid = ?');
            $statementUserProfile->execute([$uuid]);

            $statementUserSettings = $this->pdo->prepare('DELETE FROM usersettings WHERE useruuid = ?');
            $statementUserSettings->execute([$uuid]);

            $this->pdo->commit();

            return true;
        } catch (Exception $pdoException) {
            $this->pdo->rollBack();
            error_log('Could not delete account '.$uuid.': '.$pdoException->getMessage());
            return false;
        }
    }

    public function deleteIdentity($uuid, $identId): bool
    {
        $statementValidate = $this->pdo->prepare('SELECT 1 FROM UserIdentitys WHERE PrincipalID = ? AND IdentityID = ?');
        $statementValidate->execute([$uuid, $identId]);

        if($statementValidate->fetch()) {
            $statementDelete = $this->pdo->prepare('DELETE FROM UserAccounts WHERE PrincipalID = ?');
            $statementDelete->execute([$identId]);

            return true;
        }

        return false;
    }

    public function generateUuid(): string
    {
        return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            // 32 bits for "time_low"
            mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
    
            // 16 bits for "time_mid"
            mt_rand( 0, 0xffff ),
    
            // 16 bits for "time_hi_and_version",
            // four most significant bits holds version number 4
            mt_rand( 0, 0x0fff ) | 0x4000,
    
            // 16 bits, 8 bits for "clk_seq_hi_res",
            // 8 bits for "clk_seq_low",
            // two most significant bits holds zero and one for variant DCE1.1
            mt_rand( 0, 0x3fff ) | 0x8000,
    
            // 48 bits for "node"
            mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
        );
    }
}
