// https://github.com/typeorm/typeorm/issues/2400
import pg from "pg";
pg.types.setTypeParser(20, Number);
import { DataSource } from "typeorm";
import * as highlight from "cli-highlight";
import config from "../config/index.js";
import { User } from "../models/entities/user.js";
import { DriveFile } from "../models/entities/drive-file.js";
import { DriveFolder } from "../models/entities/drive-folder.js";
import { AccessToken } from "../models/entities/access-token.js";
import { App } from "../models/entities/app.js";
import { PollVote } from "../models/entities/poll-vote.js";
import { Note } from "../models/entities/note.js";
import { NoteReaction } from "../models/entities/note-reaction.js";
import { NoteWatching } from "../models/entities/note-watching.js";
import { NoteThreadMuting } from "../models/entities/note-thread-muting.js";
import { NoteUnread } from "../models/entities/note-unread.js";
import { Notification } from "../models/entities/notification.js";
import { Meta } from "../models/entities/meta.js";
import { Following } from "../models/entities/following.js";
import { Instance } from "../models/entities/instance.js";
import { Muting } from "../models/entities/muting.js";
import { RenoteMuting } from "../models/entities/renote-muting.js";
import { SwSubscription } from "../models/entities/sw-subscription.js";
import { Blocking } from "../models/entities/blocking.js";
import { UserList } from "../models/entities/user-list.js";
import { UserListJoining } from "../models/entities/user-list-joining.js";
import { UserGroup } from "../models/entities/user-group.js";
import { UserGroupJoining } from "../models/entities/user-group-joining.js";
import { UserGroupInvitation } from "../models/entities/user-group-invitation.js";
import { Hashtag } from "../models/entities/hashtag.js";
import { NoteFavorite } from "../models/entities/note-favorite.js";
import { AbuseUserReport } from "../models/entities/abuse-user-report.js";
import { RegistrationTicket } from "../models/entities/registration-tickets.js";
import { MessagingMessage } from "../models/entities/messaging-message.js";
import { Signin } from "../models/entities/signin.js";
import { AuthSession } from "../models/entities/auth-session.js";
import { FollowRequest } from "../models/entities/follow-request.js";
import { Emoji } from "../models/entities/emoji.js";
import { UserNotePining } from "../models/entities/user-note-pining.js";
import { Poll } from "../models/entities/poll.js";
import { UserKeypair } from "../models/entities/user-keypair.js";
import { UserPublickey } from "../models/entities/user-publickey.js";
import { UserProfile } from "../models/entities/user-profile.js";
import { UserSecurityKey } from "../models/entities/user-security-key.js";
import { AttestationChallenge } from "../models/entities/attestation-challenge.js";
import { Page } from "../models/entities/page.js";
import { PageLike } from "../models/entities/page-like.js";
import { GalleryPost } from "../models/entities/gallery-post.js";
import { GalleryLike } from "../models/entities/gallery-like.js";
import { ModerationLog } from "../models/entities/moderation-log.js";
import { UsedUsername } from "../models/entities/used-username.js";
import { Announcement } from "../models/entities/announcement.js";
import { AnnouncementRead } from "../models/entities/announcement-read.js";
import { Clip } from "../models/entities/clip.js";
import { ClipNote } from "../models/entities/clip-note.js";
import { Antenna } from "../models/entities/antenna.js";
import { PromoNote } from "../models/entities/promo-note.js";
import { PromoRead } from "../models/entities/promo-read.js";
import { Relay } from "../models/entities/relay.js";
import { Channel } from "../models/entities/channel.js";
import { ChannelFollowing } from "../models/entities/channel-following.js";
import { ChannelNotePining } from "../models/entities/channel-note-pining.js";
import { RegistryItem } from "../models/entities/registry-item.js";
import { PasswordResetRequest } from "../models/entities/password-reset-request.js";
import { UserPending } from "../models/entities/user-pending.js";
import { Webhook } from "../models/entities/webhook.js";
import { UserIp } from "../models/entities/user-ip.js";
import { NoteEdit } from "../models/entities/note-edit.js";
import { entities as charts } from "../services/chart/entities.js";
import { dbLogger } from "./logger.js";
import { OAuthApp } from "../models/entities/oauth-app.js";
import { OAuthToken } from "../models/entities/oauth-token.js";
import { HtmlNoteCacheEntry } from "../models/entities/html-note-cache-entry.js";
import { HtmlUserCacheEntry } from "../models/entities/html-user-cache-entry.js";
import { Bite } from "../models/entities/bite.js";
import { InteractionStamp } from "../models/entities/interaction-stamp.js";
const sqlLogger = dbLogger.createSubLogger("sql", "gray", false);
const isLogEnabled = (level)=>{
    const logLevel = config.db.logging;
    return Array.isArray(logLevel) ? logLevel.includes(level) : logLevel === level || logLevel?.trim()?.toLowerCase() === "all";
};
const isLoggingEnabled = ()=>{
    const logLevel = config.db.logging;
    return Array.isArray(logLevel) ? logLevel.length > 0 : logLevel != null;
};
class MyCustomLogger {
    highlight(sql) {
        return highlight.highlight(sql, {
            language: "sql",
            ignoreIllegals: true
        });
    }
    logQuery(query, parameters) {
        if (isLogEnabled("query")) sqlLogger.info(this.highlight(query).substring(0, 100));
    }
    logQueryError(error, query, parameters) {
        if (isLogEnabled("error")) sqlLogger.error(this.highlight(query));
    }
    logQuerySlow(time, query, parameters) {
        if (isLogEnabled("slow")) sqlLogger.warn(this.highlight(query));
    }
    logSchemaBuild(message) {
        if (isLogEnabled("schema")) sqlLogger.info(message);
    }
    log(message) {
        if (isLogEnabled("log")) sqlLogger.info(message);
    }
    logMigration(message) {
        if (isLogEnabled("info")) sqlLogger.info(message);
    }
}
export const entities = [
    Announcement,
    AnnouncementRead,
    Meta,
    Instance,
    App,
    AuthSession,
    AccessToken,
    User,
    UserProfile,
    UserKeypair,
    UserPublickey,
    UserList,
    UserListJoining,
    UserGroup,
    UserGroupJoining,
    UserGroupInvitation,
    UserNotePining,
    UserSecurityKey,
    UsedUsername,
    AttestationChallenge,
    Following,
    FollowRequest,
    Muting,
    RenoteMuting,
    Blocking,
    Note,
    NoteEdit,
    NoteFavorite,
    NoteReaction,
    NoteWatching,
    NoteThreadMuting,
    NoteUnread,
    Page,
    PageLike,
    GalleryPost,
    GalleryLike,
    DriveFile,
    DriveFolder,
    Poll,
    PollVote,
    Notification,
    Emoji,
    Hashtag,
    SwSubscription,
    AbuseUserReport,
    RegistrationTicket,
    MessagingMessage,
    Signin,
    ModerationLog,
    Clip,
    ClipNote,
    Antenna,
    PromoNote,
    PromoRead,
    Relay,
    Channel,
    ChannelFollowing,
    ChannelNotePining,
    RegistryItem,
    PasswordResetRequest,
    UserPending,
    Webhook,
    UserIp,
    OAuthApp,
    OAuthToken,
    HtmlNoteCacheEntry,
    HtmlUserCacheEntry,
    Bite,
    InteractionStamp,
    ...charts
];
const log = isLoggingEnabled() || process.env.LOG_SQL === "true";
export const db = new DataSource({
    type: "postgres",
    host: config.db.host,
    port: config.db.port,
    username: config.db.user,
    password: config.db.pass,
    database: config.db.db,
    extra: {
        statement_timeout: 1000 * 10,
        ...config.db.extra
    },
    synchronize: process.env.NODE_ENV === "test",
    dropSchema: process.env.NODE_ENV === "test",
    cache: !config.db.disableCache ? {
        type: "ioredis",
        options: {
            host: config.redis.host,
            port: config.redis.port,
            family: config.redis.family == null ? 0 : config.redis.family,
            username: config.redis.user ?? "default",
            password: config.redis.pass,
            keyPrefix: `${config.redis.prefix}:query:`,
            db: config.redis.db || 0,
            tls: config.redis.tls
        }
    } : false,
    logging: log,
    logger: new MyCustomLogger(),
    maxQueryExecutionTime: 300,
    entities: entities,
    migrations: [
        "../../migration/*.js"
    ]
});
export async function initDb(force = false) {
    if (force) {
        if (db.isInitialized) {
            await db.destroy();
        }
        await db.initialize();
        return;
    }
    if (db.isInitialized) {
    // nop
    } else {
        await db.initialize();
    }
}
export async function resetDb() {
    const reset = async ()=>{
        const { redisClient } = await import("./redis.js");
        await redisClient.flushdb();
        const tables = await db.query(`SELECT relname AS "table"
		FROM pg_class C LEFT JOIN pg_namespace N ON (N.oid = C.relnamespace)
		WHERE nspname NOT IN ('pg_catalog', 'information_schema')
			AND C.relkind = 'r'
			AND nspname !~ '^pg_toast';`);
        for (const table of tables){
            await db.query(`DELETE FROM "${table.table}" CASCADE`);
        }
    };
    for(let i = 1; i <= 3; i++){
        try {
            await reset();
        } catch (e) {
            if (i === 3) {
                throw e;
            } else {
                await new Promise((resolve)=>setTimeout(resolve, 1000));
                continue;
            }
        }
        break;
    }
}
