<?php
namespace Misuzu;

use InvalidArgumentException;
use RuntimeException;
use Index\ByteFormat;
use Misuzu\Parsers\Parser;
use Misuzu\Profile\ProfileFields;
use Misuzu\Users\User;
use Misuzu\Users\UserSession;
use Misuzu\Users\Assets\UserBackgroundAsset;

$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) && is_array($_POST);

try {
    $profileUser = User::findForProfile($userId);
} catch(RuntimeException $ex) {
    http_response_code(404);
    Template::render('profile.index');
    return;
}

if($profileUser->isDeleted()) {
    http_response_code(404);
    Template::render('profile.index');
    return;
}

$notices = [];

$profileFields = new ProfileFields($db);
$currentUser = User::getCurrent();
$viewingAsGuest = $currentUser === null;
$currentUserId = $viewingAsGuest ? 0 : $currentUser->getId();
$viewingOwnProfile = $currentUserId === $profileUser->getId();
$isBanned = $profileUser->hasActiveWarning();
$userPerms = perms_get_user($currentUserId)[MSZ_PERMS_USER];
$canManageWarnings = perms_check($userPerms, MSZ_PERM_USER_MANAGE_WARNINGS);
$canEdit = !$isBanned
    && UserSession::hasCurrent()
    && ($viewingOwnProfile || $currentUser->isSuper() || (
        perms_check($userPerms, MSZ_PERM_USER_MANAGE_USERS)
        && $currentUser->hasAuthorityOver($profileUser)
    ));

if($isEditing) {
    if(!$canEdit) {
        echo render_error(403);
        return;
    }

    $perms = perms_check_bulk($userPerms, [
        'edit_profile' => MSZ_PERM_USER_EDIT_PROFILE,
        'edit_avatar' => MSZ_PERM_USER_CHANGE_AVATAR,
        'edit_background' => MSZ_PERM_USER_CHANGE_BACKGROUND,
        'edit_about' => MSZ_PERM_USER_EDIT_ABOUT,
        'edit_birthdate' => MSZ_PERM_USER_EDIT_BIRTHDATE,
        'edit_signature' => MSZ_PERM_USER_EDIT_SIGNATURE,
    ]);

    Template::set([
        'perms' => $perms,
        'background_attachments' => UserBackgroundAsset::getAttachmentStringOptions(),
    ]);

    if(!empty($_POST) && is_array($_POST)) {
        if(!CSRF::validateRequest()) {
            $notices[] = 'Couldn\'t verify you, please refresh the page and retry.';
        } else {
            $profileFieldsSubmit = filter_input(INPUT_POST, 'profile', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);

            if(!empty($profileFieldsSubmit)) {
                if(!$perms['edit_profile']) {
                    $notices[] = 'You\'re not allowed to edit your profile';
                } else {
                    $profileFieldInfos = $profileFields->getFields();
                    $profileFieldsSetInfos = [];
                    $profileFieldsSetValues = [];
                    $profileFieldsRemove = [];

                    foreach($profileFieldInfos as $fieldInfo) {
                        $fieldName = $fieldInfo->getName();
                        $fieldValue = empty($profileFieldsSubmit[$fieldName]) ? '' : (string)filter_var($profileFieldsSubmit[$fieldName]);

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

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

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

                    if(!empty($profileFieldsRemove))
                        $profileFields->removeFieldValues($profileUser, $profileFieldsRemove);
                    if(!empty($profileFieldsSetInfos))
                        $profileFields->setFieldValues($profileUser, $profileFieldsSetInfos, $profileFieldsSetValues);
                }
            }

            if(!empty($_POST['about']) && is_array($_POST['about'])) {
                if(!$perms['edit_about']) {
                    $notices[] = 'You\'re not allowed to edit your about page.';
                } else {
                    $aboutText  = (string)($_POST['about']['text'] ?? '');
                    $aboutParse = (int)($_POST['about']['parser'] ?? Parser::PLAIN);
                    $aboutValid = User::validateProfileAbout($aboutParse, $aboutText);

                    if($aboutValid === '')
                        $profileUser->setProfileAboutText($aboutText)->setProfileAboutParser($aboutParse);
                    else switch($aboutValid) {
                        case 'parser':
                            $notices[] = 'The selected about section parser is invalid.';
                            break;
                        case 'long':
                            $notices[] = sprintf('Please keep the length of your about section below %d characters.', User::PROFILE_ABOUT_MAX_LENGTH);
                            break;
                        default:
                            $notices[] = 'Failed to update about section, contact an administator.';
                            break;
                    }
                }
            }

            if(!empty($_POST['signature']) && is_array($_POST['signature'])) {
                if(!$perms['edit_signature']) {
                    $notices[] = 'You\'re not allowed to edit your forum signature.';
                } else {
                    $sigText  = (string)($_POST['signature']['text'] ?? '');
                    $sigParse = (int)($_POST['signature']['parser'] ?? Parser::PLAIN);
                    $sigValid = User::validateForumSignature($sigParse, $sigText);

                    if($sigValid === '')
                        $profileUser->setForumSignatureText($sigText)->setForumSignatureParser($sigParse);
                    else switch($sigValid) {
                        case 'parser':
                            $notices[] = 'The selected forum signature parser is invalid.';
                            break;
                        case 'long':
                            $notices[] = sprintf('Please keep the length of your signature below %d characters.', User::FORUM_SIGNATURE_MAX_LENGTH);
                            break;
                        default:
                            $notices[] = 'Failed to update signature, contact an administator.';
                            break;
                    }
                }
            }

            if(!empty($_POST['birthdate']) && is_array($_POST['birthdate'])) {
                if(!$perms['edit_birthdate']) {
                    $notices[] = "You aren't allow to change your birthdate.";
                } else {
                    $birthYear  = (int)($_POST['birthdate']['year'] ?? 0);
                    $birthMonth = (int)($_POST['birthdate']['month'] ?? 0);
                    $birthDay   = (int)($_POST['birthdate']['day'] ?? 0);
                    $birthValid = User::validateBirthdate($birthYear, $birthMonth, $birthDay);

                    if($birthValid === '')
                        $profileUser->setBirthdate($birthYear, $birthMonth, $birthDay);
                    else switch($birthValid) {
                        case 'year':
                            $notices[] = 'The given birth year is invalid.';
                            break;
                        case 'date':
                            $notices[] = 'The given birthdate is invalid.';
                            break;
                        default:
                            $notices[] = 'Something unexpected happened while setting your birthdate.';
                            break;
                    }
                }
            }

            if(!empty($_FILES['avatar'])) {
                $avatarInfo = $profileUser->getAvatarInfo();

                if(!empty($_POST['avatar']['delete'])) {
                    $avatarInfo->delete();
                } else {
                    if(!$perms['edit_avatar']) {
                        $notices[] = 'You aren\'t allow to change your avatar.';
                    } elseif(!empty($_FILES['avatar'])
                        && is_array($_FILES['avatar'])
                        && !empty($_FILES['avatar']['name']['file'])) {
                        if($_FILES['avatar']['error']['file'] !== UPLOAD_ERR_OK) {
                            switch($_FILES['avatar']['error']['file']) {
                                case UPLOAD_ERR_NO_FILE:
                                    $notices[] = 'Select a file before hitting upload!';
                                    break;
                                case UPLOAD_ERR_PARTIAL:
                                    $notices[] = 'The upload was interrupted, please try again!';
                                    break;
                                case UPLOAD_ERR_INI_SIZE:
                                case UPLOAD_ERR_FORM_SIZE:
                                    $notices[] = sprintf('Your avatar is not allowed to be larger in file size than %s!', ByteFormat::format($avatarInfo->getMaxBytes()));
                                    break;
                                default:
                                    $notices[] = 'Unable to save your avatar, contact an administator!';
                                    break;
                            }
                        } else {
                            try {
                                $avatarInfo->setFromPath($_FILES['avatar']['tmp_name']['file']);
                            } 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!', $avatarInfo->getMaxWidth(), $avatarInfo->getMaxHeight()),
                                    'File size of $path is too large.' => sprintf('Your avatar is not allowed to be larger in file size than %s!', ByteFormat::format($avatarInfo->getMaxBytes())),
                                    default => $exMessage,
                                };
                            } catch(RuntimeException $ex) {
                                $notices[] = 'Unable to save your avatar, contact an administator!';
                            }
                        }
                    }
                }
            }

            if(!empty($_FILES['background'])) {
                $backgroundInfo = $profileUser->getBackgroundInfo();

                if((int)($_POST['background']['attach'] ?? -1) === 0) {
                    $backgroundInfo->delete();
                } else {
                    if(!$perms['edit_background']) {
                        $notices[] = 'You aren\'t allow to change your background.';
                    } elseif(!empty($_FILES['background']) && is_array($_FILES['background'])) {
                        if(!empty($_FILES['background']['name']['file'])) {
                            if($_FILES['background']['error']['file'] !== UPLOAD_ERR_OK) {
                                switch($_FILES['background']['error']['file']) {
                                    case UPLOAD_ERR_NO_FILE:
                                        $notices[] = 'Select a file before hitting upload!';
                                        break;
                                    case UPLOAD_ERR_PARTIAL:
                                        $notices[] = 'The upload was interrupted, please try again!';
                                        break;
                                    case UPLOAD_ERR_INI_SIZE:
                                    case UPLOAD_ERR_FORM_SIZE:
                                        $notices[] = sprintf('Your background is not allowed to be larger in file size than %s!', ByteFormat::format($backgroundProps['max_size']));
                                        break;
                                    default:
                                        $notices[] = 'Unable to save your background, contact an administator!';
                                        break;
                                }
                            } else {
                                try {
                                    $backgroundInfo->setFromPath($_FILES['background']['tmp_name']['file']);
                                } 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!', $backgroundInfo->getMaxWidth(), $backgroundInfo->getMaxHeight()),
                                        'File size of $path is too large.' => sprintf('Your background is not allowed to be larger in file size than %2$s!', ByteFormat::format($backgroundInfo->getMaxBytes())),
                                        default => $exMessage,
                                    };
                                } catch(RuntimeException $ex) {
                                    $notices[] = 'Unable to save your background, contact an administator!';
                                }
                            }
                        }

                        $backgroundInfo->setAttachment((int)($_POST['background']['attach'] ?? 0))
                            ->setBlend(!empty($_POST['background']['attr']['blend']))
                            ->setSlide(!empty($_POST['background']['attr']['slide']));
                    }
                }
            }

            $profileUser->saveProfile();
        }

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

$profileStats = DB::prepare('
    SELECT (
        SELECT COUNT(`topic_id`)
        FROM `msz_forum_topics`
        WHERE `user_id` = u.`user_id`
        AND `topic_deleted` IS NULL
    ) AS `forum_topic_count`,
    (
        SELECT COUNT(`post_id`)
        FROM `msz_forum_posts`
        WHERE `user_id` = u.`user_id`
        AND `post_deleted` IS NULL
    ) AS `forum_post_count`,
    (
        SELECT COUNT(`change_id`)
        FROM `msz_changelog_changes`
        WHERE `user_id` = u.`user_id`
    ) AS `changelog_count`,
    (
        SELECT COUNT(`comment_id`)
        FROM `msz_comments_posts`
        WHERE `user_id` = u.`user_id`
        AND `comment_deleted` IS NULL
    ) AS `comments_count`
    FROM `msz_users` AS u
    WHERE `user_id` = :user_id
')->bind('user_id', $profileUser->getId())->fetch();

switch($profileMode) {
    default:
        echo render_error(404);
        return;

    case 'forum-topics':
        $template = 'profile.topics';
        $topicsCount = forum_topic_count_user($profileUser->getId(), $currentUserId);
        $topicsPagination = new Pagination($topicsCount, 20);

        if(!$topicsPagination->hasValidOffset()) {
            echo render_error(404);
            return;
        }

        $topics = forum_topic_listing_user(
            $profileUser->getId(), $currentUserId,
            $topicsPagination->getOffset(), $topicsPagination->getRange()
        );

        Template::set([
            'title' => $profileUser->getUsername() . ' / topics',
            'canonical_url' => url('user-profile-forum-topics', ['user' => $profileUser->getId(), 'page' => Pagination::param()]),
            'profile_topics' => $topics,
            'profile_topics_pagination' => $topicsPagination,
        ]);
        break;

    case 'forum-posts':
        $template = 'profile.posts';
        $postsCount = forum_post_count_user($profileUser->getId());
        $postsPagination = new Pagination($postsCount, 20);

        if(!$postsPagination->hasValidOffset()) {
            echo render_error(404);
            return;
        }

        $posts = forum_post_listing(
            $profileUser->getId(),
            $postsPagination->getOffset(),
            $postsPagination->getRange(),
            false,
            true
        );

        Template::set([
            'title' => $profileUser->getUsername() . ' / posts',
            'canonical_url' => url('user-profile-forum-posts', ['user' => $profileUser->getId(), 'page' => Pagination::param()]),
            'profile_posts' => $posts,
            'profile_posts_pagination' => $postsPagination,
        ]);
        break;

    case '':
        $template = 'profile.index';
        $warnings = $profileUser->getProfileWarnings($currentUser);

        Template::set([
            'profile_warnings' => $warnings,
            'profile_warnings_view_private' => $canManageWarnings,
            'profile_warnings_can_manage' => $canManageWarnings,
        ]);

        if(!$viewingAsGuest) {
            $activeCategoryStats = forum_get_user_most_active_category_info($profileUser->getId());
            $activeCategoryInfo = empty($activeCategoryStats->forum_id) ? null : forum_get($activeCategoryStats->forum_id);

            $activeTopicStats = forum_get_user_most_active_topic_info($profileUser->getId());
            $activeTopicInfo = empty($activeTopicStats->topic_id) ? null : forum_topic_get($activeTopicStats->topic_id);

            $profileFieldValues = $profileFields->getFieldValues($profileUser);
            $profileFieldInfos = $profileFieldInfos ?? $profileFields->getFields(fieldValueInfos: $isEditing ? null : $profileFieldValues);
            $profileFieldFormats = $profileFields->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->getFieldId() === $fieldInfo->getId()) {
                        $fieldValue = $fieldValueTest;
                        break;
                    }

                $fieldName = $fieldInfo->getName();

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

                    $profileFieldRawValues[$fieldName] = $fieldValue->getValue();
                    $profileFieldDisplayValues[$fieldName] = $fieldFormat->formatDisplay($fieldValue->getValue());
                    if($fieldFormat->hasLinkFormat())
                        $profileFieldLinkValues[$fieldName] = $fieldFormat->formatLink($fieldValue->getValue());
                }
            }

            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,
            ]);
        }
        break;
}

if(!empty($template)) {
    Template::render($template, [
        'profile_viewer' => $currentUser,
        'profile_user' => $profileUser,
        '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,
    ]);
}
