<?php

require_once "lib/upload.php";
require_once "lib/account.php";
/**
 * Adds a meme to the database with associated image and text content.
 *
 * @param string $title The title of the meme.
 * @param string $memeText The text content of the meme.
 * @param int $imageID The ID of the image associated with the meme.
 * @return array Returns an associative array with the operation status and a message.
 * @global mysqli $mysqli The database connection object.
 */
function addMeme(string $title, string $memeText, int $imageID): array
{
    global $mysqli;
    $output = ["Status" => "Fail"];
    if (isLoggedIn() && fileExists($imageID, false) && !empty($title) && !empty($memeText) && !empty($imageID) && $imageID > 0) {
        $stmtMemeAdd = $mysqli->prepare('INSERT INTO Memes (AuthorID, Title, TextContent, FileID) VALUES (?, ?, ?, ?)');
        $stmtMemeAdd->bind_param('issi', $_SESSION['ID'], htmlspecialchars($title), htmlspecialchars($memeText), $imageID);
        if ($stmtMemeAdd->execute() && $stmtMemeAdd->affected_rows > 0) {
            $output["Status"] = "Success";
            $output["Meme"] = "Funny";
        }
    }
    return $output;
}

function executeAndRenderMemes(mysqli_stmt $stmt): string {
    global $routerConfig;
    $stmt->execute();
    $stmt->store_result();
    $stmt->bind_result($memeID, $title, $textContent, $createdAt, $authorID, $filePath, $imageWidth, $imageHeight, $userNickname);

    $memes_out = '';
    $meme_template = file_get_contents($routerConfig['template_dir'] . "meme.html");
    $meme_gallery_template = file_get_contents($routerConfig['template_dir'] . 'meme_gallery.html');

    while ($stmt->fetch()) {
        $memes_out .= renderMeme($memeID, $authorID, $title, $textContent, $createdAt, $filePath, $imageWidth, $imageHeight, $userNickname, $meme_template);
    }

    $meme_add = isLoggedIn() ? file_get_contents($routerConfig['template_dir'] . 'meme_add.html') : '';
    $meme_gallery_out = str_replace('__TEMPLATE_MEMES_HERE__', $memes_out, $meme_gallery_template);
    $meme_gallery_out = str_replace('__TEMPLATE_MEME_ADD__', $meme_add, $meme_gallery_out);

    $stmt->close();
    return $meme_gallery_out;
}

/**
 * Renders a meme into HTML based on provided data and a template.
 *
 * @param int $id The ID of the meme.
 * @param int $authorId The author's user ID.
 * @param string $title The title of the meme.
 * @param string $textContent The text content of the meme.
 * @param string $createdAt The creation timestamp of the meme.
 * @param string $filePath The file path of the associated image.
 * @param int $imageWidth The width of the image.
 * @param int $imageHeight The height of the image.
 * @param string $userNickname The nickname of the meme's author.
 * @param string $meme_template The HTML template for a meme. (used to not read the template over and over when rendering more memes)
 * @return string Returns the rendered HTML of the meme.
 */
function renderMeme(int $id, int $authorId, string $title, string $textContent, string $createdAt, string $filePath, int $imageWidth, int $imageHeight, string $userNickname, string $meme_template): string
{

    $meme_out = str_replace('__TEMPLATE_MEME_TITLE__', htmlspecialchars($title), $meme_template);
    $meme_out = str_replace('__TEMPLATE_MEME_AUTHOR__', htmlspecialchars($userNickname), $meme_out);
    $meme_out = str_replace('__TEMPLATE_MEME_DATE__', htmlspecialchars($createdAt), $meme_out);
    $meme_out = str_replace('__TEMPLATE_MEME_IMAGE__', '/' . htmlspecialchars($filePath), $meme_out);
    $meme_out = str_replace('__TEMPLATE_MEME_IMAGE_WIDTH__', strval($imageWidth), $meme_out);
    $meme_out = str_replace('__TEMPLATE_MEME_IMAGE_HEIGHT__', strval($imageHeight), $meme_out);
    $meme_out = str_replace('__TEMPLATE_MEME_DELETE_BUTTON__', (isModerator() || $_SESSION['ID'] == $authorId) ? "<button onclick=\"deleteMeme($id);\"><i class='ri-delete-bin-line'></i></button>" : '', $meme_out);

    $meme_votes = calculateNetVotes($id);
    $meme_net_votes = $meme_votes['NetVotes'];

    if ($meme_votes['UserVote'] > 0) {
        $meme_upvote_active = 'fill';
        $meme_downvote_active = 'line';
        $meme_vote_counter_class = 'positive';
        $meme_upvote_button_class = ' visual_hover';
        $meme_downvote_button_class = '';
    } elseif (($meme_votes['UserVote'] < 0)) {
        $meme_upvote_active = 'line';
        $meme_downvote_active = 'fill';
        $meme_vote_counter_class = 'negative';
        $meme_upvote_button_class = '';
        $meme_downvote_button_class = ' visual_hover';
    } else {
        $meme_downvote_active = 'line';
        $meme_upvote_active = 'line';
        $meme_vote_counter_class = 'neutral';
        $meme_upvote_button_class = '';
        $meme_downvote_button_class = '';
    }
    $meme_upvote = isLoggedIn() ? "<button id='meme_votes_upvote_button_$id' class='meme_upvote$meme_upvote_button_class' onclick=\"voteMeme($id, 1);\"> <i id='meme_votes_upvote_$id' class=\"ri-arrow-up-circle-$meme_upvote_active\"></i></button>" : '';
    $meme_downvote = isLoggedIn() ? "<button id='meme_votes_downvote_button_$id' class='meme_downvote$meme_downvote_button_class' onclick=\"voteMeme($id, 0);\"> <i id='meme_votes_downvote_$id' class=\"ri-arrow-down-circle-$meme_downvote_active\"></i></button>" : '';

    $meme_out = str_replace('__TEMPLATE_MEME_VOTES_NUMBER__', strval($meme_net_votes), $meme_out);
    $meme_out = str_replace('__TEMPLATE_MEME_UPVOTE__', $meme_upvote, $meme_out);
    $meme_out = str_replace('__TEMPLATE_MEME_DOWNVOTE__', $meme_downvote, $meme_out);
    $meme_out = str_replace('__TEMPLATE_MEME_ID__', strval($id), $meme_out);
    $meme_out = str_replace('__TEMPLATE_MEME_VOTE_COUNTER_CLASS__', $meme_vote_counter_class, $meme_out);

    return str_replace('__TEMPLATE_MEME_TEXT__', htmlspecialchars($textContent), $meme_out);
}

/**
 * Renders a gallery of memes, optionally filtered by author ID, meme ID, or a keyword.
 *
 * This function retrieves memes from the database and returns an HTML string representation.
 * It supports filtering by author ID, meme ID, or a keyword that is searched in titles and text content.
 * It also supports pagination through an offset parameter.
 *
 * @param int|null $offset Pagination offset, used to calculate the starting point for records to return.
 * @param int|null $authorId Optional author ID for filtering memes by a specific author.
 * @param int|null $memeId Optional meme ID for rendering a single meme.
 * @param string|null $keyword Optional keyword for full-text search in meme titles and content.
 * @return string Returns the complete HTML content of the meme gallery, optionally filtered.
 */

function getMemeGallery(?int $offset = null, ?int $authorId = null, ?int $memeId = null, ?string $keyword = null): array {
    return [
        "Status" => "Success",
        "Output" => renderMemeGallery($offset, $authorId, $memeId, $keyword)
    ];
}

function renderMemeGallery(?int $offset = null, ?int $authorId = null, ?int $memeId = null, ?string $keyword = null): string {
    global $mysqli, $routerConfig;

    // Start building the SQL query
    $query = 'SELECT Memes.ID, Memes.Title, Memes.TextContent, Memes.CreatedAt, Memes.AuthorID, 
              Files.Path, Files.Width, Files.Height, Users.Nickname
              FROM Memes
              INNER JOIN Users ON Memes.AuthorID = Users.ID
              INNER JOIN Files ON Memes.FileID = Files.ID';

    $conditions = [];
    $params = [];
    $types = '';

    // Add conditions based on provided parameters
    if ($authorId !== null) {
        $conditions[] = 'Memes.AuthorID = ?';
        $params[] = $authorId;
        $types .= 'i';
    }
    if ($memeId !== null) {
        $conditions[] = 'Memes.ID = ?';
        $params[] = $memeId;
        $types .= 'i';
    }
    if ($keyword !== null) {
        $conditions[] = '(Memes.Title LIKE CONCAT("%", ?, "%") OR Memes.TextContent LIKE CONCAT("%", ?, "%"))';
        $params[] = $keyword;
        $params[] = $keyword;
        $types .= 'ss';
    }

    // Append conditions to the query
    if (!empty($conditions)) {
        $query .= ' WHERE ' . join(' AND ', $conditions);
    }

    if($offset == null) {
        $offset = 0;
    }
    // Add pagination and limit
    $query .= ' LIMIT ? OFFSET ?';
    $params[] = $routerConfig['meme']['per_page'];
    $params[] = $routerConfig['meme']['per_page'] * $offset;
    $types .= 'ii';

    $stmt = $mysqli->prepare($query);
    $stmt->bind_param($types, ...$params);
    return executeAndRenderMemes($stmt);
}


/**
 * Deletes a meme from the database if the current user has the right permissions.
 *
 * @param int $memeID The ID of the meme to delete.
 * @return array Returns an associative array with the status of the operation.
 * @global mysqli $mysqli The database connection object.
 */
function deleteMeme(int $memeID): array
{
    global $mysqli;
    $out = ["Status" => "Fail"];
    if (isLoggedIn()) {
        $query = !isModerator() ? 'DELETE FROM Memes WHERE ID = ? AND AuthorID = ?' : 'DELETE FROM Memes WHERE ID = ?';
        $stmtDelete = $mysqli->prepare($query);
        if (!isModerator()) {
            $stmtDelete->bind_param('ii', $memeID, $_SESSION['ID']);
        } else {
            $stmtDelete->bind_param('i', $memeID);
        }
        $stmtDelete->execute();
        if ($stmtDelete->affected_rows > 0) {
            $out['Status'] = 'Success';
        }
        $stmtDelete->close();
    }
    return $out;
}

/**
 * Records or updates a vote on a meme by the current user.
 *
 * @param int $memeID The ID of the meme to be voted on.
 * @param int $isUpvote Indicates whether the vote is an upvote (1) or downvote (0).
 * @return array Returns an associative array with the status of the vote operation.
 * @global mysqli $mysqli The database connection object.
 */
function voteMeme(int $memeID, int $isUpvote): array
{
    global $mysqli;
    $out = ["Status" => "Fail"];
    if ($isUpvote != 1) {
        $isUpvote = 0;
    }
    $memeVoteConn = $mysqli->prepare('INSERT INTO MemeVotes (MemeID, UserID, isUpvote) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE isUpvote = VALUES(isUpvote)');
    $memeVoteConn->bind_param('iii', $memeID, $_SESSION['ID'], $isUpvote);
    $memeVoteConn->execute();
    if ($memeVoteConn->affected_rows > 0) {
        $out['Status'] = 'Success';
    }
    $memeVoteConn->close();
    return $out;
}

/**
 * Deletes a vote previously made by the current user on a meme.
 *
 * @param int $memeID The ID of the meme whose vote is to be deleted.
 * @return array Returns an associative array with the status of the deletion.
 * @global mysqli $mysqli The database connection object.
 */
function deleteVoteMeme(int $memeID): array
{
    global $mysqli;
    $out = ["Status" => "Fail"];
    $memeVoteConn = $mysqli->prepare('DELETE FROM MemeVotes WHERE MemeID = ? AND UserID = ?');
    $memeVoteConn->bind_param('ii', $memeID, $_SESSION['ID']);
    $memeVoteConn->execute();
    if ($memeVoteConn->affected_rows > 0) {
        $out['Status'] = 'Success';
    }
    $memeVoteConn->close();
    return $out;
}

/**
 * Calculates the net votes for a meme and determines if the current user has voted on it.
 * The array has both the net votes and the user vote(0 when the user hasn't voted)
 *
 * @param int $memeID The ID of the meme for which votes are being calculated.
 * @return array Returns an array with net votes and the user's vote status.
 * @global mysqli $mysqli The database connection object.
 */
function calculateNetVotes(int $memeID): array
{
    global $mysqli;

    // Adjusted query to calculate net votes and get the user's vote in one go
    $query = "
        SELECT 
            SUM(CASE WHEN isUpvote = 1 THEN 1 ELSE -1 END) AS NetVotes,
            (
                SELECT CASE WHEN isUpvote = 1 THEN 1 ELSE -1 END
                FROM MemeVotes 
                WHERE MemeID = ? AND UserID = ?
            ) AS UserVote
        FROM MemeVotes 
        WHERE MemeID = ?";

    $stmt = $mysqli->prepare($query);
    $userID = $_SESSION['ID'];
    $stmt->bind_param('iii', $memeID, $userID, $memeID);
    $stmt->execute();
    $result = $stmt->get_result();

    $data = $result->fetch_assoc();

    $netVotes = $data['NetVotes'] ?? 0; // Null coalescing operator in case no votes are found
    $userVote = $data['UserVote'] ?? 0; // Default to 0 if the user hasn't voted

    $stmt->close();

    return [
        "NetVotes" => $netVotes,
        "UserVote" => $userVote
    ];
}

/**
 * Fetches the net votes and user's vote status for a specific meme.
 * Essentially just a wrapper of getMemeVotes for an API call
 *
 * @param int $memeID The ID of the meme to fetch votes for.
 * @return array Returns an array with the net votes and the user's vote status, along with operation status.
 */
function getMemeVotes(int $memeID): array
{
    $voteData = calculateNetVotes($memeID);
    return [
        "Status" => "Success",
        "NetVotes" => $voteData['NetVotes'],
        "UserVote" => $voteData['UserVote']
    ];
}