<?php
use Index\Data\IDbConnection;
use Index\Data\Migration\IDbMigration;

final class NewPermissionsSystem_20230830_213930 implements IDbMigration {
    public function migrate(IDbConnection $conn): void {
        // make sure cron doesn't fuck us over
        $conn->execute('DELETE FROM msz_config WHERE config_name = "perms.needsRecalc"');

        $conn->execute('
            CREATE TABLE msz_perms (
                user_id        INT(10) UNSIGNED        NULL DEFAULT NULL,
                role_id        INT(10) UNSIGNED        NULL DEFAULT NULL,
                forum_id       INT(10) UNSIGNED        NULL DEFAULT NULL,
                perms_category VARBINARY(64)       NOT NULL,
                perms_allow    BIGINT(20) UNSIGNED NOT NULL,
                perms_deny     BIGINT(20) UNSIGNED NOT NULL,
                UNIQUE KEY perms_unique (user_id, role_id, forum_id, perms_category),
                KEY perms_user_foreign (user_id),
                KEY perms_role_foreign (role_id),
                KEY perms_forum_foreign (forum_id),
                KEY perms_category_index (perms_category),
                CONSTRAINT perms_user_foreign
                    FOREIGN KEY (user_id)
                    REFERENCES msz_users (user_id)
                    ON UPDATE CASCADE
                    ON DELETE CASCADE,
                CONSTRAINT perms_role_foreign
                    FOREIGN KEY (role_id)
                    REFERENCES msz_roles (role_id)
                    ON UPDATE CASCADE
                    ON DELETE CASCADE,
                CONSTRAINT perms_forum_foreign
                    FOREIGN KEY (forum_id)
                    REFERENCES msz_forum_categories (forum_id)
                    ON UPDATE CASCADE
                    ON DELETE CASCADE
            ) ENGINE=InnoDB COLLATE=utf8mb4_bin
        ');

        $conn->execute('
            ALTER TABLE msz_perms
            ADD CONSTRAINT perms_53bit
                CHECK (perms_allow >= 0 AND perms_deny >= 0 AND perms_allow <= 9007199254740991 AND perms_deny <= 9007199254740991),
            ADD CONSTRAINT perms_only_user_or_role
                CHECK ((user_id IS NULL AND role_id IS NULL) OR (user_id IS NULL AND role_id IS NOT NULL) OR (user_id IS NOT NULL AND role_id IS NULL))
        ');

        $conn->execute('
            CREATE TABLE msz_perms_calculated (
                user_id          INT(10) UNSIGNED        NULL DEFAULT NULL,
                forum_id         INT(10) UNSIGNED        NULL DEFAULT NULL,
                perms_category   VARBINARY(64)       NOT NULL,
                perms_calculated BIGINT(20) UNSIGNED NOT NULL,
                UNIQUE KEY perms_calculated_unique (user_id, forum_id, perms_category),
                KEY perms_calculated_user_foreign (user_id),
                KEY perms_calculated_forum_foreign (forum_id),
                KEY perms_calculated_category_index (perms_category),
                CONSTRAINT perms_calculated_user_foreign
                    FOREIGN KEY (user_id)
                    REFERENCES msz_users (user_id)
                    ON UPDATE CASCADE
                    ON DELETE CASCADE,
                CONSTRAINT perms_calculated_forum_foreign
                    FOREIGN KEY (forum_id)
                    REFERENCES msz_forum_categories (forum_id)
                    ON UPDATE CASCADE
                    ON DELETE CASCADE
            ) ENGINE=InnoDB COLLATE=utf8mb4_bin
        ');

        $conn->execute('
            ALTER TABLE msz_perms_calculated
            ADD CONSTRAINT perms_calculated_53bit
                CHECK (perms_calculated >= 0 AND perms_calculated <= 9007199254740991)
        ');

        $insert = $conn->prepare('INSERT INTO msz_perms (user_id, role_id, forum_id, perms_category, perms_allow, perms_deny) VALUES (?, ?, ?, ?, ?, ?)');

        $result = $conn->query('SELECT user_id, role_id, general_perms_allow, general_perms_deny, user_perms_allow, user_perms_deny, changelog_perms_allow, changelog_perms_deny, news_perms_allow, news_perms_deny, forum_perms_allow, forum_perms_deny, comments_perms_allow, comments_perms_deny FROM msz_permissions');
        while($result->next()) {
            $insert->addParameter(1, $result->isNull(0) ? null : $result->getString(0));
            $insert->addParameter(2, $result->isNull(1) ? null : $result->getString(1));
            $insert->addParameter(3, null);
            $insert->addParameter(4, 'user');
            $insert->addParameter(5, $result->getInteger(4));
            $insert->addParameter(6, $result->getInteger(5));
            $insert->execute();

            $allow = $result->getInteger(2);
            $allow |= $result->getInteger(6) << 8;
            $allow |= $result->getInteger(8) << 16;
            $allow |= $result->getInteger(10) << 24;
            $allow |= $result->getInteger(12) << 32;

            $deny = $result->getInteger(3);
            $deny |= $result->getInteger(7) << 8;
            $deny |= $result->getInteger(9) << 16;
            $deny |= $result->getInteger(11) << 24;
            $deny |= $result->getInteger(13) << 32;

            $insert->addParameter(4, 'global');
            $insert->addParameter(5, $allow);
            $insert->addParameter(6, $deny);
            $insert->execute();
        }

        $result = $conn->query('SELECT user_id, role_id, forum_id, forum_perms_allow, forum_perms_deny FROM msz_forum_permissions');
        while($result->next()) {
            $insert->addParameter(1, $result->isNull(0) ? null : $result->getString(0));
            $insert->addParameter(2, $result->isNull(1) ? null : $result->getString(1));
            $insert->addParameter(3, $result->getString(2));
            $insert->addParameter(4, 'forum');
            $insert->addParameter(5, $result->getInteger(3));
            $insert->addParameter(6, $result->getInteger(4));
            $insert->execute();
        }

        $conn->execute('DROP TABLE msz_forum_permissions');
        $conn->execute('DROP TABLE msz_permissions');

        // schedule recalc
        $conn->execute('INSERT INTO msz_config (config_name, config_value) VALUES ("perms.needsRecalc", "b:1;")');
    }
}
