<?php

function makePathSafe($userInput): string
{
    // Keep only alphanumeric characters, underscores, and hyphens
    $safeString = preg_replace('/[^\w\-]/', '', $userInput);

    // Ensure no path traversal
    $safeString = str_replace('..', '_', $safeString);

    // Trim leading/trailing underscores
    $safeString = trim($safeString, '_');

    // Replace directory separator characters with underscores
    $safeString = str_replace(['/', '\\'], '_', $safeString);

    // Limit length for safety
    return substr($safeString, 0, 255);
}

function autoRotateImage(Imagick $imagick): void {
    // Get the current orientation of the image
    try {
        $orientation = $imagick->getImageOrientation();
        switch ($orientation) {
            case Imagick::ORIENTATION_BOTTOMRIGHT: // upside down
                $imagick->rotateimage("#000", 180); // rotate 180 degrees
                break;

            case Imagick::ORIENTATION_RIGHTTOP: // 90 degrees CW
                $imagick->rotateimage("#000", 90); // rotate 90 degrees CW
                break;

            case Imagick::ORIENTATION_LEFTBOTTOM: // 90 degrees CCW
                $imagick->rotateimage("#000", -90); // rotate 90 degrees CCW
                break;
        }

        // Reset orientation to normal after the correction
        $imagick->setImageOrientation(Imagick::ORIENTATION_TOPLEFT);
    } catch (ImagickException) {
    }

}

function getIncomingFiles(): array
{
    $files = $_FILES;
    $files2 = [];
    foreach ($files as $infoArr) {
        $filesByInput = [];
        foreach ($infoArr as $key => $valueArr) {
            if (is_array($valueArr)) { // file input "multiple"
                foreach ($valueArr as $i => $value) {
                    $filesByInput[$i][$key] = $value;
                }
            } else { // -> string, normal file input
                $filesByInput[] = $infoArr;
                break;
            }
        }
        $files2 = array_merge($files2, $filesByInput);
    }
    $files3 = [];
    foreach ($files2 as $file) { // let's filter empty & errors
        if (!$file['error']) $files3[] = $file;
    }
    return $files3;
}

function saveUploadedFileInDatabase(string $filePath, string $fileType, int $width, int $height): bool
{
    global $mysqli;
    $stmt = $mysqli->prepare("INSERT INTO Files (Path, Type, UploadedBy, UploadedAt, Width, Height) VALUES (?, ?, ?, NOW(), ?, ?)");
    $stmt->bind_param("ssiii", $filePath, $fileType, $_SESSION["ID"], $width, $height);
    $stmt->execute();
    $stat = $stmt->affected_rows > 0;
    $stmt->close();
    return $stat;
}

function doImageUpload($inFile, $outFile): bool
{
    // Create Imagick object
    $width = 0;
    $height = 0;
    try {
        $imagick = new Imagick($inFile);
        $imagick->setImageFormat('webp');
        autoRotateImage($imagick);
        $imagick->stripImage();
        $imagick->writeImage($outFile);
        $width = $imagick->getImageWidth();
        $height = $imagick->getImageHeight();
        $imagick->destroy();
    } catch (ImagickException) {
    }

    // Check if the reencoding was successful
    if (file_exists($outFile)) {
        return saveUploadedFileInDatabase($outFile, 'image/webp', $width, $height);
    } else {
        return false;
    }
}

function listFiles($onlyMine = true): array
{
    $output = ["Status" => "Success"];
    require_once "lib/account.php";
    if (isLoggedIn()) {
        global $mysqli;
        if (!$onlyMine && !isModerator()) {
            $onlyMine = true;
        }
        $query = "SELECT Files.ID, Files.Path, Files.Type, Files.UploadedAt, Files.UploadedBy, Users.Nickname FROM Files INNER JOIN Users ON Files.UploadedBy = Users.ID ORDER BY UploadedAt DESC";

        if ($onlyMine) {
            $query .= " WHERE UploadedBy = ?";
        }

        $stmt = $mysqli->prepare($query);
        if ($onlyMine) {
            $stmt->bind_param("i", $_SESSION["ID"]);
        }

        $id = 0;
        $path = "";
        $type = "";
        $uploadedAt = "";
        $uploadedByID = 0;
        $uploadedBy = "";

        $stmt->bind_result($id, $path, $type, $uploadedAt, $uploadedByID, $uploadedBy);

        $stmt->execute();
        $files = array();
        // Fetch the results into the bound variables
        while ($stmt->fetch()) {
            $files[] = [
                'ID' => $id,
                'Path' => $path,
                'Type' => $type,
                'UploadedAt' => $uploadedAt,
                'UploadedByID' => $uploadedByID,
                'UploadedBy' => $uploadedBy,
            ];
        }

        // Check if any results were fetched
        $output["Files"] = $files;

        $stmt->close();
    }

    return $output;

}

function parseIncomingFiles(): array
{
    $incomingFiles = getIncomingFiles();
    $success = true;
    foreach ($incomingFiles as $incomingFile) {
        if ($incomingFile["error"] == 0 && is_file($incomingFile["tmp_name"])) {
            $type = explode("/", $incomingFile["type"]);
            if ($type[0] == "image") {
                $imgFname = pathinfo($incomingFile["name"], PATHINFO_FILENAME);
                $uploadPath = getUploadPath("image", $imgFname);
                if (!empty($uploadPath)) {
                    if (!doImageUpload($incomingFile["tmp_name"], $uploadPath)) {
                        $success = false;
                    }
                } else {
                    $success = false;
                }
            }
        }
    }
    $output = ["Status" => "Fail"];
    if ($success) {
        $output["Status"] = "Success";
    }
    return $output;
}

function getUploadPath($type = "unknown", $filename = "hehe"): string
{
    $type = makePathSafe($type);
    $id = makePathSafe($_SESSION["ID"]);
    $date = makePathSafe(date("YmdHis"));
    $filename = makePathSafe($filename);
    $extension = match ($type) {
        'image' => 'webp',
        default => 'dummy',
    };
    if ($extension != "dummy") {
        $basepath = "uploads/$type/$id/$date";
        mkdir($basepath, 755, true);
        return $basepath . "/$filename.$extension";
    } else {
        return "";
    }
}

function fileExists(int $fileId, bool $onlyMine = true): bool|string
{
    if (!$fileId) {
        return false;
    }
    global $mysqli;
    if (!$onlyMine && !isModerator()) {
        $onlyMine = true;
    }
    $query = 'SELECT ID, Path FROM Files WHERE ID = ?' . ($onlyMine ? ' AND UploadedBy = ?' : '');
    $stmtfileexists = $mysqli->prepare($query);
    if ($onlyMine) {
        $stmtfileexists->bind_param('ii', $fileId, $_SESSION['ID']);
    } else {
        $stmtfileexists->bind_param('i', $fileId);
    }
    $filePath = "";
    $id = null;
    $stmtfileexists->bind_result($id, $filePath);
    $stmtfileexists->execute();
    $stmtfileexists->fetch();
    if ($id != null) {
        return $filePath;
    } else {
        return false;
    }
}

function addToGroup(int $groupId, int $fileId): array
{
    $output = ["Status" => "Fail"];
    if (!$groupId || !$fileId) {
        return $output;
    }
    global $mysqli;
    $stmtcheck = $mysqli->prepare('SELECT ID FROM FileGroups WHERE CreatorID != ? AND ID = ?');
    $stmtcheck->bind_param('ii', $_SESSION['ID'], $groupId);
    $stmtcheck->execute();
    if ($stmtcheck->affected_rows == 0) {
        if (fileExists($fileId, false)) {
            $stmtadd = $mysqli->prepare('INSERT INTO FileGroups (FileID, CreatorID, ID) VALUES (?, ?, ?)');
            $stmtadd->bind_param('iii', $fileId, $_SESSION['ID'], $groupId);
            $stmtadd->execute();
            if ($stmtadd->affected_rows > 0) {
                $output["Status"] = "Success";
            }
        }
    }
    return $output;
}

function deleteFile(int $fileID): array
{
    global $mysqli;
    $out = ["Status" => "Fail"];
    if (isLoggedIn()) {
        $file_location = fileExists($fileID, !isAdmin());
        $query = !isAdmin() ? 'DELETE FROM Files WHERE ID = ? AND UploadedBy = ?' : 'DELETE FROM Files WHERE ID = ?';
        $stmtDelete = $mysqli->prepare($query);
        if (!isAdmin()) {
            $stmtDelete->bind_param('ii', $fileID, $_SESSION['ID']);
        } else {
            $stmtDelete->bind_param('i', $fileID);
        }
        $stmtDelete->execute();
        if ($file_location) {
            if (unlink($file_location) && $stmtDelete->affected_rows > 0) {
                $out['Status'] = 'Success';
            }
        }
    }
    return $out;
}