<?php
namespace Misuzu;

use stdClass;
use InvalidArgumentException;
use RuntimeException;
use Index\ByteFormat;
use Index\Http\Content\MultipartFormContent;
use Index\Http\Content\Multipart\FileMultipartFormData;
use Misuzu\Forum\ForumSignaturesData;
use Misuzu\Parsers\TextFormat;
use Misuzu\Profile\{ProfileAboutData,ProfileBackgroundAttach};
use Misuzu\Users\{User,UserBirthdatesData};
use Misuzu\Users\Assets\UserAvatarAsset;
use Misuzu\Users\Assets\UserBackgroundAsset;

if(!isset($msz) || !($msz instanceof \Misuzu\MisuzuContext))
    die('Script must be called through the Misuzu route dispatcher.');

$userId = !empty($_GET['u']) && is_string($_GET['u']) ? trim($_GET['u']) : 0;
$profileMode = !empty($_GET['m']) && is_string($_GET['m']) ? (string)$_GET['m'] : '';
$isEditing = !empty($_GET['edit']) && is_string($_GET['edit']) ? (bool)$_GET['edit'] : !empty($_POST);

$viewerInfo = $msz->authInfo->userInfo;
$viewingAsGuest = $viewerInfo === null;
$viewerId = $viewingAsGuest ? '0' : $viewerInfo->id;

try {
    $userInfo = $msz->usersCtx->getUserInfo($userId, 'profile');
} catch(RuntimeException $ex) {
    $userId = $msz->usersCtx->namesHistory->resolvePastUserName($userId);
    if($userId !== null) {
        header(sprintf('Location: %s', $msz->routingCtx->urls->format('user-profile', ['user' => $userId])));
        return;
    }

    http_response_code(404);
    Template::render('profile.index', [
        'profile_is_guest' => $viewingAsGuest,
        'profile_is_deleted' => true,
        'profile_is_banned' => false,
    ]);
    return;
}

if($userInfo->deleted) {
    http_response_code(404);
    Template::render('profile.index', [
        'profile_is_guest' => $viewingAsGuest,
        'profile_is_deleted' => true,
        'profile_is_banned' => false,
    ]);
    return;
}

switch($profileMode) {
    default:
        Template::throwError(404);

    case 'forum-topics':
        Tools::redirect($msz->routingCtx->urls->format('search-query', ['query' => sprintf('type:forum:topic author:%s', $userInfo->name), 'section' => 'topics']));
        return;

    case 'forum-posts':
        Tools::redirect($msz->routingCtx->urls->format('search-query', ['query' => sprintf('type:forum:post author:%s', $userInfo->name), 'section' => 'posts']));
        return;

    case '':
        break;
}

$notices = [];

$userRank = $msz->usersCtx->getUserRank($userInfo);
$viewerRank = $msz->usersCtx->getUserRank($viewerInfo);

$viewerPermsGlobal = $msz->authInfo->getPerms('global');
$viewerPermsUser = $msz->authInfo->getPerms('user');

$activeBanInfo = $msz->usersCtx->tryGetActiveBan($userInfo);
$isBanned = $activeBanInfo !== null;
$viewingOwnProfile = (string)$viewerId === $userInfo->id;
$canManageWarnings = $viewerPermsUser->check(Perm::U_WARNINGS_MANAGE);
$canEdit = !$viewingAsGuest && ((!$isBanned && $viewingOwnProfile) || $viewerInfo->super || (
    $viewerPermsUser->check(Perm::U_USERS_MANAGE) && ($viewingOwnProfile || $viewerRank > $userRank)
));
$avatarAsset = new UserAvatarAsset($userInfo);
$backgroundInfo = $msz->profileCtx->backgrounds->getProfileBackground($userInfo);
$backgroundAsset = new UserBackgroundAsset($userInfo);

$aboutInfo = $msz->profileCtx->about->getProfileAbout($userInfo);
$sigInfo = $msz->forumCtx->signatures->getSignature($userInfo);

if($isEditing) {
    if(!$canEdit)
        Template::throwError(403);

    $perms = $viewerPermsUser->checkMany([
        'edit_profile' => Perm::U_PROFILE_EDIT,
        'edit_avatar' => Perm::U_AVATAR_CHANGE,
        'edit_background' => Perm::U_PROFILE_BACKGROUND_CHANGE,
        'edit_about' => Perm::U_PROFILE_ABOUT_EDIT,
        'edit_birthdate' => Perm::U_PROFILE_BIRTHDATE_EDIT,
        'edit_signature' => Perm::U_FORUM_SIGNATURE_EDIT,
    ]);

    Template::set([
        'perms' => $perms,
        'birthdate_info' => $msz->usersCtx->birthdates->getUserBirthdate($userInfo),
    ]);

    if(!empty($_POST)) {
        if(!$msz->csrfCtx->verifyLegacy()) {
            $notices[] = "Couldn't verify you, please refresh the page and retry.";
        } else {
            if(!$perms->edit_profile) {
                $notices[] = "You're not allowed to edit your profile.";
            } else {
                $profileFieldInfos = iterator_to_array($msz->profileCtx->fields->getFields());
                $profileFieldsSetInfos = [];
                $profileFieldsSetValues = [];
                $profileFieldsRemove = [];

                foreach($profileFieldInfos as $fieldInfo) {
                    $fieldName = sprintf('profile_%s', $fieldInfo->name);
                    $fieldValue = empty($_POST[$fieldName]) || !is_scalar($_POST[$fieldName])
                        ? '' : (string)filter_var($_POST[$fieldName]);

                    if(empty($_POST[$fieldName])) {
                        $profileFieldsRemove[] = $fieldInfo;
                        continue;
                    }

                    if($fieldInfo->checkValue($fieldValue)) {
                        $profileFieldsSetInfos[] = $fieldInfo;
                        $profileFieldsSetValues[] = $fieldValue;
                    } else
                        $notices[] = sprintf("%s isn't properly formatted.", $fieldInfo->title);

                    unset($fieldName, $fieldValue, $fieldInfo);
                }

                if(!empty($profileFieldsRemove))
                    $msz->profileCtx->fields->removeFieldValues($userInfo, $profileFieldsRemove);
                if(!empty($profileFieldsSetInfos))
                    $msz->profileCtx->fields->setFieldValues($userInfo, $profileFieldsSetInfos, $profileFieldsSetValues);
            }

            if(isset($_POST['about_body']) && is_scalar($_POST['about_body'])) {
                if(!$perms->edit_about) {
                    $notices[] = "You're not allowed to edit your about page.";
                } else {
                    $aboutBody = (string)$_POST['about_body'];
                    if(trim($aboutBody) === '') {
                        $msz->profileCtx->about->deleteProfileAbout($userInfo);
                        $aboutInfo = null;
                    } else {
                        $aboutFormat = TextFormat::tryFrom(isset($_POST['about_format']) && is_scalar($_POST['about_format']) ? (string)$_POST['about_format'] : '');
                        $aboutValid = ProfileAboutData::validateProfileAbout($aboutFormat, $aboutBody);
                        if($aboutValid === '')
                            $aboutInfo = $msz->profileCtx->about->updateProfileAbout($userInfo, $aboutBody, $aboutFormat);
                        else
                            $notices[] = ProfileAboutData::validateProfileAboutText($aboutValid);
                    }
                }
            }

            if(isset($_POST['sig_body']) && is_scalar($_POST['sig_body'])) {
                if(!$perms->edit_signature) {
                    $notices[] = "You're not allowed to edit your forum signature.";
                } else {
                    $sigBody = (string)$_POST['sig_body'];
                    if(trim($sigBody) === '') {
                        $msz->forumCtx->signatures->deleteSignature($userInfo);
                        $sigInfo = null;
                    } else {
                        $sigFormat = TextFormat::tryFrom(isset($_POST['sig_format']) && is_scalar($_POST['sig_format']) ? (string)$_POST['sig_format'] : '');
                        $sigValid = ForumSignaturesData::validateSignature($sigFormat, $sigBody);
                        if($sigValid === '')
                            $sigInfo = $msz->forumCtx->signatures->updateSignature($userInfo, $sigBody, $sigFormat);
                        else
                            $notices[] = ForumSignaturesData::validateSignatureText($sigValid);
                    }
                }
            }

            if(!empty($_POST['birth_day']) && !empty($_POST['birth_month'])) {
                if(!$perms->edit_birthdate) {
                    $notices[] = "You aren't allow to change your birthdate.";
                } else {
                    $birthYear  = (int)($_POST['birth_year'] ?? 0);
                    $birthMonth = (int)$_POST['birth_month'];
                    $birthDay   = (int)$_POST['birth_day'];
                    $birthValid = UserBirthdatesData::validateBirthdate($birthYear, $birthMonth, $birthDay);

                    if($birthValid === '') {
                        if($birthMonth === 0 && $birthDay === 0)
                            $msz->usersCtx->birthdates->deleteUserBirthdate($userInfo);
                        else
                            $msz->usersCtx->birthdates->updateUserBirthdate($userInfo, $birthYear === 0 ? null : $birthYear, $birthMonth, $birthDay);
                    } else
                        $notices[] = UserBirthdatesData::validateBirthdateText($birthValid);
                }
            }

            if(!empty($_POST['avatar_delete'])) {
                $avatarAsset->delete();
            } elseif(isset($mszRequestContent) && $mszRequestContent instanceof MultipartFormContent) {
                $avatarInfo = $mszRequestContent->getParamData('avatar_file');
                if($avatarInfo instanceof FileMultipartFormData) {
                    if(!$perms->edit_avatar) {
                        $notices[] = "You aren't allow to change your avatar.";
                    } elseif($avatarInfo->getSize() > 0) {
                        $avatarTemp = tempnam(sys_get_temp_dir(), 'msz-legacy-avatar-');
                        try {
                            $avatarInfo->moveTo($avatarTemp);
                            $avatarAsset->setFromPath($avatarTemp);
                        } catch(InvalidArgumentException $ex) {
                            $exMessage = $ex->getMessage();
                            $notices[] = match($exMessage) {
                                '$path is not a valid image.' => 'The file you uploaded was not an image!',
                                '$path is not an allowed image file.' => 'This type of image is not supported, keep to PNG, JPG or GIF!',
                                'Dimensions of $path are too large.' => sprintf("Your avatar can't be larger than %dx%d!", $avatarAsset->getMaxWidth(), $avatarAsset->getMaxHeight()),
                                'File size of $path is too large.' => sprintf('Your avatar is not allowed to be larger in file size than %s!', ByteFormat::format($avatarAsset->getMaxBytes())),
                                default => $exMessage,
                            };
                        } catch(RuntimeException $ex) {
                            $notices[] = 'Unable to save your avatar, contact an administator!';
                        } finally {
                            if(is_file($avatarTemp))
                                unlink($avatarTemp);
                        }
                    }
                }
            }

            if(isset($_POST['bg_attach']) && is_scalar($_POST['bg_attach'])) {
                $bgFormat = ProfileBackgroundAttach::tryFrom((string)$_POST['bg_attach']);

                if($bgFormat === null) {
                    $backgroundAsset->delete();
                    $msz->profileCtx->backgrounds->deleteProfileBackground($userInfo);
                    $backgroundAsset = null;
                } else {
                    if(!$perms->edit_background) {
                        $notices[] = "You aren't allow to change your background.";
                    } elseif(isset($mszRequestContent) && $mszRequestContent instanceof MultipartFormContent) {
                        $bgInfo = $mszRequestContent->getParamData('bg_file');
                        if($bgInfo instanceof FileMultipartFormData && $bgInfo->getSize() > 0) {
                            $bgTemp = tempnam(sys_get_temp_dir(), 'msz-legacy-profile-background-');
                            try {
                                $bgInfo->moveTo($bgTemp);
                                $backgroundAsset->setFromPath($bgTemp);
                            } catch(InvalidArgumentException $ex) {
                                $exMessage = $ex->getMessage();
                                $notices[] = match($exMessage) {
                                    '$path is not a valid image.' => 'The file you uploaded was not an image!',
                                    '$path is not an allowed image file.' => 'This type of image is not supported, keep to PNG, JPG or GIF!',
                                    'Dimensions of $path are too large.' => sprintf("Your background can't be larger than %dx%d!", $backgroundAsset->getMaxWidth(), $backgroundAsset->getMaxHeight()),
                                    'File size of $path is too large.' => sprintf('Your background is not allowed to be larger in file size than %s!', ByteFormat::format($backgroundAsset->getMaxBytes())),
                                    default => $exMessage,
                                };
                            } catch(RuntimeException $ex) {
                                $notices[] = 'Unable to save your background, contact an administator!';
                            } finally {
                                if(is_file($bgTemp))
                                    unlink($bgTemp);
                            }
                        }

                        $backgroundInfo = $msz->profileCtx->backgrounds->updateProfileBackground(
                            $userInfo,
                            $bgFormat,
                            !empty($_POST['bg_blend']),
                            !empty($_POST['bg_slide'])
                        );
                    }
                }

                $backgroundAsset = new UserBackgroundAsset($userInfo);
            }
        }

        // Unset $isEditing and hope the user doesn't refresh their profile!
        if(empty($notices))
            $isEditing = false;
    }
}

// TODO: create user counters so these can be statically kept
$profileStats = new stdClass;
$profileStats->forum_topic_count = $msz->forumCtx->countTotalUserTopics($userInfo);
$profileStats->forum_post_count = $msz->forumCtx->countTotalUserPosts($userInfo);
$profileStats->comments_count = $msz->commentsCtx->posts->countPosts(userInfo: $userInfo, deleted: false);

if(!$viewingAsGuest) {
    Template::set('profile_warnings', iterator_to_array($msz->usersCtx->warnings->getWarningsWithDefaultBacklog($userInfo)));

    if((!$isBanned || $canEdit)) {
        $unranked = $msz->config->getValues([
            'forum_leader.unranked.forum:a',
            'forum_leader.unranked.topic:a',
        ]);

        $activeCategoryStats = $msz->forumCtx->categories->getMostActiveCategoryInfo(
            $userInfo,
            $unranked['forum_leader.unranked.forum'],
            $unranked['forum_leader.unranked.topic'],
            deleted: false
        );
        $activeCategoryInfo = $activeCategoryStats->success ? $msz->forumCtx->categories->getCategory(categoryId: $activeCategoryStats->categoryId) : null;

        $activeTopicStats = $msz->forumCtx->topics->getMostActiveTopicInfo(
            $userInfo,
            $unranked['forum_leader.unranked.forum'],
            $unranked['forum_leader.unranked.topic'],
            deleted: false
        );
        $activeTopicInfo = $activeTopicStats->success ? $msz->forumCtx->topics->getTopic(topicId: $activeTopicStats->topicId) : null;

        $profileFieldValues = iterator_to_array($msz->profileCtx->fields->getFieldValues($userInfo));
        $profileFieldInfos = $profileFieldInfos ?? iterator_to_array($msz->profileCtx->fields->getFields(fieldValueInfos: $isEditing ? null : $profileFieldValues));
        $profileFieldFormats = iterator_to_array($msz->profileCtx->fields->getFieldFormats(fieldValueInfos: $profileFieldValues));

        $profileFieldRawValues = [];
        $profileFieldLinkValues = [];
        $profileFieldDisplayValues = [];

        // using field infos as the basis for now, uses the correct ordering
        foreach($profileFieldInfos as $fieldInfo) {
            unset($fieldValue);

            foreach($profileFieldValues as $fieldValueTest)
                if($fieldValueTest->fieldId === $fieldInfo->id) {
                    $fieldValue = $fieldValueTest;
                    break;
                }

            $fieldName = $fieldInfo->name;

            if(isset($fieldValue)) {
                foreach($profileFieldFormats as $fieldFormatTest)
                    if($fieldFormatTest->id === $fieldValue->formatId) {
                        $fieldFormat = $fieldFormatTest;
                        break;
                    }

                if(!isset($fieldFormat))
                    continue;

                $profileFieldRawValues[$fieldName] = $fieldValue->value;
                $profileFieldDisplayValues[$fieldName] = $fieldFormat->formatDisplay($fieldValue->value);
                if($fieldFormat->linkFormat !== null)
                    $profileFieldLinkValues[$fieldName] = $fieldFormat->formatLink($fieldValue->value);
            }
        }

        Template::set([
            'profile_active_category_stats' => $activeCategoryStats,
            'profile_active_category_info' => $activeCategoryInfo,
            'profile_active_topic_stats' => $activeTopicStats,
            'profile_active_topic_info' => $activeTopicInfo,
            'profile_fields_infos' => $profileFieldInfos,
            'profile_fields_raw_values' => $profileFieldRawValues,
            'profile_fields_display_values' => $profileFieldDisplayValues,
            'profile_fields_link_values' => $profileFieldLinkValues,
        ]);
    }
}

Template::render('profile.index', [
    'profile_viewer' => $viewerInfo,
    'profile_user' => $userInfo,
    'profile_colour' => $msz->usersCtx->getUserColour($userInfo),
    'profile_stats' => $profileStats,
    'profile_mode' => $profileMode,
    'profile_notices' => $notices,
    'profile_can_edit' => $canEdit,
    'profile_is_editing' => $isEditing,
    'profile_is_banned' => $isBanned,
    'profile_is_guest' => $viewingAsGuest,
    'profile_is_deleted' => false,
    'profile_ban_info' => $activeBanInfo,
    'profile_avatar_asset' => $avatarAsset,
    'profile_background_info' => $backgroundInfo,
    'profile_background_asset' => $backgroundAsset,
    'profile_can_send_messages' => $viewerPermsGlobal->check(Perm::G_MESSAGES_SEND),
    'profile_age' => $msz->usersCtx->birthdates->getUserAge($userInfo),
    'profile_about_info' => $aboutInfo,
    'profile_forum_signature_info' => $sigInfo,
]);