<?php
namespace Misuzu;

use ZipArchive;
use Index\XString;
use Misuzu\Users\UserInfo;

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

if(!$msz->authInfo->loggedIn)
    Template::throwError(401);

$dbConn = $msz->dbCtx->conn;

/** @param string[] $fieldInfos */
function db_to_zip(
    ZipArchive $archive,
    UserInfo $userInfo,
    string $baseName,
    array $fieldInfos,
    string $userIdField = 'user_id'
): string {
    global $dbConn;

    $userId = $userInfo->id;
    $fields = [];

    foreach($fieldInfos as $key => $fieldInfo) {
        $fieldInfo = explode(':', $fieldInfo);
        $fieldInfo = [
            'name' => $fieldInfo[0],
            'type' => $fieldInfo[1] ?? 'n',
            'nullable' => ($fieldInfo[2] ?? '') === 'n',
        ];
        $fieldInfo['is_null'] = $fieldInfo['type'] === 'n';

        if(!$fieldInfo['is_null']) {
            $selectField = $fieldInfo['name'];

            if($fieldInfo['type'] === 'a')
                $selectField = sprintf('INET6_NTOA(%s)', $selectField);
            elseif($fieldInfo['type'] === 't')
                $selectField = sprintf('UNIX_TIMESTAMP(%s)', $selectField);
            elseif($fieldInfo['type'] === 'h')
                $selectField = sprintf('HEX(%s)', $selectField);

            $fields[] = $selectField;
        }

        $fieldInfos[$key] = $fieldInfo;
    }

    $tmpName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . sprintf('msz-user-data-%s-%s-%s.tmp', $userId, $baseName, XString::random(8));
    $tmpHandle = fopen($tmpName, 'wb');

    try {
        $stmt = $dbConn->prepare(sprintf('SELECT %s FROM msz_%s WHERE %s = ?', implode(', ', $fields), $baseName, $userIdField));
        $stmt->nextParameter($userId);
        $stmt->execute();
        $result = $stmt->getResult();

        while($result->next()) {
            $args = -1;
            $row = [];

            foreach($fieldInfos as $fieldInfo) {
                $fieldValue = null;

                if(!$fieldInfo['is_null']) {
                    ++$args;

                    if(!$fieldInfo['nullable'] || !$result->isNull($args)) {
                        if($fieldInfo['type'] === 's' || $fieldInfo['type'] === 'a' || $fieldInfo['type'] === 'j' || $fieldInfo['type'] === 'h') {
                            $fieldValue = $result->getString($args);

                            if($fieldInfo['type'] === 'j')
                                $fieldValue = json_decode($fieldValue);
                        } elseif($fieldInfo['type'] === 't' || $fieldInfo['type'] === 'b' || $fieldInfo['type'] === 'i') {
                            $fieldValue = $result->getInteger($args);

                            if($fieldInfo['type'] === 'b')
                                $fieldValue = $fieldValue !== 0;
                            elseif($fieldInfo['type'] === 't') {
                                $fieldValue = date(DATE_ATOM, $fieldValue);
                                if(str_ends_with($fieldValue, '+00:00'))
                                    $fieldValue = substr($fieldValue, 0, -6) . 'Z';
                            }
                        } else {
                            $fieldValue = '?';
                        }
                    }
                }

                $row[$fieldInfo['name']] = $fieldValue;
            }

            fwrite($tmpHandle, json_encode($row, JSON_INVALID_UTF8_SUBSTITUTE));
            fwrite($tmpHandle, "\n");
        }
    } finally {
        fflush($tmpHandle);
        fclose($tmpHandle);
    }

    $archive->addFile($tmpName, $baseName . '.jsonl');

    return $tmpName;
}

$errors = [];
$userInfo = $msz->authInfo->userInfo;

if(isset($_POST['action']) && is_string($_POST['action'])) {
    if(isset($_POST['password']) && is_string($_POST['password'])
        && ($msz->usersCtx->passwords->getUserPassword($userInfo)?->verifyPassword($_POST['password'] ?? ''))) {
        switch($_POST['action']) {
            case 'data':
                $msz->logsCtx->createAuthedLog('PERSONAL_DATA_DOWNLOAD');

                $timeStamp = floor(time() / 3600) * 3600;
                $fileName = sprintf('msz-user-data-%d-%d.zip', $userInfo->id, $timeStamp);
                $filePath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $fileName;
                $archive = new ZipArchive;

                if(!is_file($filePath)) {
                    if($archive->open($filePath, ZipArchive::CREATE | ZIPARCHIVE::OVERWRITE) === true) {
                        $tmpFiles = [];

                        try {
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'apps',                   ['app_id:s', 'user_id:s:n', 'app_name:s', 'app_summary:s', 'app_website:s', 'app_type:s', 'app_access_lifetime:i:n', 'app_refresh_lifetime:i:n', 'app_client_secret:n', 'app_created:t', 'app_updated:t', 'app_deleted:t:n']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'audit_log',              ['log_id:i', 'user_id:s:n', 'log_action:s', 'log_params:j', 'log_created:t', 'log_remote_addr:a:n', 'log_country:s']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'auth_tfa',               ['user_id:s', 'tfa_token:n', 'tfa_created:t']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'changelog_changes',      ['change_id:s', 'user_id:s:n', 'change_action:s:n', 'change_created:t', 'change_log:s', 'change_text:s:n']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_categories',    ['category_id:s', 'category_name:s', 'user_id:s:n', 'category_created:t', 'category_locked:t:n'], 'user_id');
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_posts',         ['comment_id:s', 'category_id:s', 'user_id:s:n', 'comment_reply_to:s:n', 'comment_text:s', 'comment_created:t', 'comment_pinned:t:n', 'comment_edited:t:n', 'comment_deleted:t:n']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'comments_votes',         ['comment_id:s', 'user_id:s', 'comment_vote:i']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_posts',            ['post_id:s', 'topic_id:s', 'forum_id:s', 'user_id:s:n', 'post_remote_addr:a', 'post_text:s', 'post_text_format:s', 'post_display_signature:b', 'post_created:t', 'post_edited:t:n', 'post_deleted:t:n']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_signatures',       ['user_id:s', 'signature_body:s', 'signature_body_format:s', 'signature_created:t']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics',           ['topic_id:s', 'forum_id:s', 'user_id:s:n', 'topic_type:i', 'topic_title:s', 'topic_count_views:i', 'topic_created:t', 'topic_bumped:t', 'topic_deleted:t:n', 'topic_locked:t:n']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics_redirects', ['topic_id:s', 'user_id:s:n', 'redir_url:s', 'redir_created:t']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'forum_topics_track',     ['user_id:s', 'topic_id:s', 'forum_id:s', 'track_last_read:t']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'login_attempts',         ['attempt_id:s', 'user_id:s:n', 'attempt_success:b', 'attempt_remote_addr:a', 'attempt_country:s', 'attempt_created:t', 'attempt_user_agent:s']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'messages',               ['msg_id:s', 'msg_owner_id:s', 'msg_author_id:s:n', 'msg_recipient_id:s:n', 'msg_reply_to:s:n', 'msg_title:s', 'msg_body:s', 'msg_body_format:s', 'msg_created:t', 'msg_sent:t:n', 'msg_read:t:n', 'msg_deleted:t:n'], 'msg_owner_id');
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'news_posts',             ['post_id:s', 'category_id:s', 'user_id:s:n', 'post_featured:b', 'post_title:s', 'post_text:s', 'post_scheduled:t', 'post_created:t', 'post_updated:t', 'post_deleted:t:n']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'oauth2_access',          ['acc_id:s', 'app_id:s', 'user_id:s:n', 'acc_token:n', 'acc_scope:s', 'acc_created:t', 'acc_expires:t']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'oauth2_authorise',       ['auth_id:s', 'app_id:s', 'user_id:s', 'uri_id:s', 'auth_challenge_code:n', 'auth_challenge_method:s', 'auth_scope:s', 'auth_code:n', 'auth_created:t', 'auth_expires:t']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'oauth2_device',          ['dev_id:s', 'app_id:s', 'user_id:s:n', 'dev_code:n', 'dev_user_code:n', 'dev_interval:i', 'dev_polled:t', 'dev_scope:s', 'dev_approval:s', 'dev_created:t', 'dev_expires:t']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'oauth2_refresh',         ['ref_id:s', 'app_id:s', 'user_id:s:n', 'acc_id:s:n', 'ref_token:n', 'ref_scope:s', 'ref_created:t', 'ref_expires:t']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'perms',                  ['user_id:s:n', 'role_id:s:n', 'forum_id:s:n', 'perms_category:s', 'perms_allow:i', 'perms_deny:i']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'perms_calculated',       ['user_id:s:n', 'forum_id:s:n', 'perms_category:s', 'perms_calculated:i']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'profile_about',          ['user_id:s', 'about_body:s', 'about_body_format:s', 'about_created:t']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'profile_backgrounds',    ['user_id:s', 'bg_attach:s', 'bg_blend:i', 'bg_slide:i']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'profile_fields_values',  ['field_id:s', 'user_id:s', 'format_id:s', 'field_value:s']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'sessions',               ['session_id:s', 'user_id:s', 'session_key:n', 'session_remote_addr_first:a', 'session_remote_addr_last:a:n', 'session_user_agent:s', 'session_country:s', 'session_expires:t', 'session_expires_bump:b', 'session_created:t', 'session_active:t:n']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'storage_tasks',          ['task_id:s', 'task_secret:n', 'user_id:s', 'pool_id:s', 'task_state:s', 'task_ip:a', 'task_name:s', 'task_size:i', 'task_type:s', 'task_hash:h', 'task_error:s', 'task_created:t']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'storage_uploads',        ['upload_id:s', 'user_id:s', 'pool_id:s', 'upload_secret:s', 'upload_name:s', 'upload_ip:a', 'upload_created:t', 'upload_accessed:t']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'users',                  ['user_id:s', 'user_name:s', 'user_email:s', 'user_remote_addr_first:a', 'user_remote_addr_last:a', 'user_super:b', 'user_country:s', 'user_colour:i:n', 'user_title:s:n', 'user_display_role_id:s:n', 'user_created:t', 'user_active:t:n', 'user_deleted:t:n']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_bans',             ['ban_id:s', 'user_id:s', 'mod_id:n', 'ban_severity:i', 'ban_reason_public:s', 'ban_reason_private:s', 'ban_created:t', 'ban_expires:t:n']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_birthdates',       ['user_id:s', 'birth_year:i:n', 'birth_month:i', 'birth_day:i']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_passwords',        ['user_id:s', 'password_hash:n', 'password_created:t']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_password_resets',  ['reset_id:s', 'user_id:s', 'reset_remote_addr:a', 'reset_requested:t', 'reset_code:n']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_roles',            ['user_id:s', 'role_id:s']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_totp',             ['user_id:s', 'totp_secret:n', 'totp_created:t']);
                            $tmpFiles[] = db_to_zip($archive, $userInfo, 'users_warnings',         ['warn_id:s', 'user_id:s', 'mod_id:n', 'warn_body:s', 'warn_created:t']);

                            $archive->close();
                        } finally {
                            foreach($tmpFiles as $tmpFile)
                                if(is_file($tmpFile))
                                    unlink($tmpFile);
                        }
                    } else {
                        $errors[] = 'Something went wrong while creating your account archive.';
                        break;
                    }
                }

                header('Content-Type: application/zip');
                header(sprintf('Content-Disposition: inline; filename="%s"', $fileName));
                echo file_get_contents($filePath);
                return;

            case 'deactivate':
                // deactivation
                break;
        }
    } else {
        $errors[] = 'Incorrect password.';
    }
}

Template::render('settings.data', [
    'errors' => $errors,
]);