"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
    return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (g && (g = 0, op[0] && (_ = 0)), _) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.notificationMessageReadPayloadSchema = exports.notificationMessagePayloadSchema = exports.notificationMessageListQuerySchema = exports.NOTIFICATION_MESSAGE_TABLE = void 0;
exports.ensureNotificationMessagesTable = ensureNotificationMessagesTable;
exports.listNotificationMessages = listNotificationMessages;
exports.getNotificationMessage = getNotificationMessage;
exports.createNotificationMessage = createNotificationMessage;
exports.selectNotificationMessageIdsToDelete = selectNotificationMessageIdsToDelete;
exports.deleteOlderNotificationMessagesBySource = deleteOlderNotificationMessagesBySource;
exports.updateNotificationMessageReadState = updateNotificationMessageReadState;
exports.deleteNotificationMessage = deleteNotificationMessage;
var zod_1 = require("zod");
var client_js_1 = require("../db/client.js");
exports.NOTIFICATION_MESSAGE_TABLE = 'notification_messages';
var notificationMessagePrioritySchema = zod_1.z.enum(['low', 'normal', 'high', 'urgent']);
var notificationMessageListStatusSchema = zod_1.z.enum(['all', 'unread']);
exports.notificationMessageListQuerySchema = zod_1.z.object({
    status: notificationMessageListStatusSchema.default('all'),
    limit: zod_1.z.coerce.number().int().min(1).max(100).default(20),
});
exports.notificationMessagePayloadSchema = zod_1.z.object({
    title: zod_1.z.string().trim().min(1).max(200),
    body: zod_1.z.string().trim().min(1).max(20000),
    category: zod_1.z.string().trim().min(1).max(60).default('general'),
    source: zod_1.z.string().trim().min(1).max(80).default('system'),
    priority: notificationMessagePrioritySchema.default('normal'),
    metadata: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).default({}),
});
exports.notificationMessageReadPayloadSchema = zod_1.z.object({
    read: zod_1.z.boolean().default(true),
});
function normalizePreviewText(value) {
    var normalized = value
        .replace(/```[\s\S]*?```/g, ' ')
        .replace(/!\[[^\]]*\]\([^)]+\)/g, ' ')
        .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1')
        .replace(/[#>*_`~-]/g, ' ')
        .replace(/\s+/g, ' ')
        .trim();
    return normalized.length > 140 ? "".concat(normalized.slice(0, 137).trimEnd(), "...") : normalized;
}
function mapNotificationMessageRow(row) {
    var _a, _b, _c, _d, _e, _f, _g;
    var body = String((_a = row.body) !== null && _a !== void 0 ? _a : '');
    var metadata = typeof row.metadata_json === 'object' && row.metadata_json ? row.metadata_json : {};
    var metadataPreview = typeof metadata.previewText === 'string'
        ? metadata.previewText
        : typeof metadata.listPreviewText === 'string'
            ? metadata.listPreviewText
            : '';
    return {
        id: Number((_b = row.id) !== null && _b !== void 0 ? _b : 0),
        title: String((_c = row.title) !== null && _c !== void 0 ? _c : ''),
        body: body,
        preview: normalizePreviewText(metadataPreview || body),
        category: String((_d = row.category) !== null && _d !== void 0 ? _d : 'general'),
        source: String((_e = row.source) !== null && _e !== void 0 ? _e : 'system'),
        priority: notificationMessagePrioritySchema.catch('normal').parse(row.priority),
        read: Boolean(row.is_read),
        readAt: row.read_at === null || row.read_at === undefined ? null : String(row.read_at),
        metadata: metadata,
        createdAt: String((_f = row.created_at) !== null && _f !== void 0 ? _f : ''),
        updatedAt: String((_g = row.updated_at) !== null && _g !== void 0 ? _g : ''),
    };
}
function resolveInsertedId(result) {
    if (typeof result === 'number' && Number.isInteger(result) && result > 0) {
        return result;
    }
    if (Array.isArray(result)) {
        var first = result[0];
        if (typeof first === 'number' && Number.isInteger(first) && first > 0) {
            return first;
        }
        if (first && typeof first === 'object' && 'id' in first) {
            var id = Number(first.id);
            return Number.isInteger(id) && id > 0 ? id : null;
        }
    }
    if (result && typeof result === 'object' && 'id' in result) {
        var id = Number(result.id);
        return Number.isInteger(id) && id > 0 ? id : null;
    }
    return null;
}
function supportsReturning() {
    var _a;
    var clientName = String((_a = client_js_1.db.client.config.client) !== null && _a !== void 0 ? _a : '').toLowerCase();
    return ['pg', 'postgres', 'postgresql', 'sqlite3', 'better-sqlite3', 'oracledb', 'mssql'].includes(clientName);
}
function ensureNotificationMessagesTable() {
    return __awaiter(this, void 0, void 0, function () {
        var hasTable, requiredColumns, _loop_1, _i, requiredColumns_1, _a, columnName, createColumn;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0: return [4 /*yield*/, client_js_1.db.schema.hasTable(exports.NOTIFICATION_MESSAGE_TABLE)];
                case 1:
                    hasTable = _b.sent();
                    if (!!hasTable) return [3 /*break*/, 3];
                    return [4 /*yield*/, client_js_1.db.schema.createTable(exports.NOTIFICATION_MESSAGE_TABLE, function (table) {
                            table.increments('id').primary();
                            table.string('title', 200).notNullable();
                            table.text('body').notNullable();
                            table.string('category', 60).notNullable().defaultTo('general');
                            table.string('source', 80).notNullable().defaultTo('system');
                            table.string('priority', 20).notNullable().defaultTo('normal');
                            table.boolean('is_read').notNullable().defaultTo(false);
                            table.timestamp('read_at', { useTz: true }).nullable();
                            table.jsonb('metadata_json').notNullable().defaultTo('{}');
                            table.timestamp('created_at', { useTz: true }).notNullable().defaultTo(client_js_1.db.fn.now());
                            table.timestamp('updated_at', { useTz: true }).notNullable().defaultTo(client_js_1.db.fn.now());
                        })];
                case 2:
                    _b.sent();
                    _b.label = 3;
                case 3:
                    requiredColumns = [
                        ['title', function (table) { return table.string('title', 200).notNullable().defaultTo('알림'); }],
                        ['body', function (table) { return table.text('body').notNullable().defaultTo(''); }],
                        ['category', function (table) { return table.string('category', 60).notNullable().defaultTo('general'); }],
                        ['source', function (table) { return table.string('source', 80).notNullable().defaultTo('system'); }],
                        ['priority', function (table) { return table.string('priority', 20).notNullable().defaultTo('normal'); }],
                        ['is_read', function (table) { return table.boolean('is_read').notNullable().defaultTo(false); }],
                        ['read_at', function (table) { return table.timestamp('read_at', { useTz: true }).nullable(); }],
                        ['metadata_json', function (table) { return table.jsonb('metadata_json').notNullable().defaultTo('{}'); }],
                        ['created_at', function (table) { return table.timestamp('created_at', { useTz: true }).notNullable().defaultTo(client_js_1.db.fn.now()); }],
                        ['updated_at', function (table) { return table.timestamp('updated_at', { useTz: true }).notNullable().defaultTo(client_js_1.db.fn.now()); }],
                    ];
                    _loop_1 = function (columnName, createColumn) {
                        var hasColumn;
                        return __generator(this, function (_c) {
                            switch (_c.label) {
                                case 0: return [4 /*yield*/, client_js_1.db.schema.hasColumn(exports.NOTIFICATION_MESSAGE_TABLE, columnName)];
                                case 1:
                                    hasColumn = _c.sent();
                                    if (!!hasColumn) return [3 /*break*/, 3];
                                    return [4 /*yield*/, client_js_1.db.schema.alterTable(exports.NOTIFICATION_MESSAGE_TABLE, function (table) {
                                            createColumn(table);
                                        })];
                                case 2:
                                    _c.sent();
                                    _c.label = 3;
                                case 3: return [2 /*return*/];
                            }
                        });
                    };
                    _i = 0, requiredColumns_1 = requiredColumns;
                    _b.label = 4;
                case 4:
                    if (!(_i < requiredColumns_1.length)) return [3 /*break*/, 7];
                    _a = requiredColumns_1[_i], columnName = _a[0], createColumn = _a[1];
                    return [5 /*yield**/, _loop_1(columnName, createColumn)];
                case 5:
                    _b.sent();
                    _b.label = 6;
                case 6:
                    _i++;
                    return [3 /*break*/, 4];
                case 7: return [2 /*return*/];
            }
        });
    });
}
function listNotificationMessages(query) {
    return __awaiter(this, void 0, void 0, function () {
        var parsedQuery, builder, rows, unreadCountResult;
        var _a;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0: return [4 /*yield*/, ensureNotificationMessagesTable()];
                case 1:
                    _b.sent();
                    parsedQuery = exports.notificationMessageListQuerySchema.parse(query);
                    builder = (0, client_js_1.db)(exports.NOTIFICATION_MESSAGE_TABLE)
                        .select('*')
                        .orderBy('is_read', 'asc')
                        .orderBy('created_at', 'desc')
                        .orderBy('id', 'desc')
                        .limit(parsedQuery.limit);
                    if (parsedQuery.status === 'unread') {
                        builder.where({ is_read: false });
                    }
                    return [4 /*yield*/, builder];
                case 2:
                    rows = _b.sent();
                    return [4 /*yield*/, (0, client_js_1.db)(exports.NOTIFICATION_MESSAGE_TABLE)
                            .where({ is_read: false })
                            .count({ count: '*' })
                            .first()];
                case 3:
                    unreadCountResult = _b.sent();
                    return [2 /*return*/, {
                            items: rows.map(function (row) { return mapNotificationMessageRow(row); }),
                            unreadCount: Number((_a = unreadCountResult === null || unreadCountResult === void 0 ? void 0 : unreadCountResult.count) !== null && _a !== void 0 ? _a : 0),
                        }];
            }
        });
    });
}
function getNotificationMessage(id) {
    return __awaiter(this, void 0, void 0, function () {
        var row;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, ensureNotificationMessagesTable()];
                case 1:
                    _a.sent();
                    return [4 /*yield*/, (0, client_js_1.db)(exports.NOTIFICATION_MESSAGE_TABLE).where({ id: id }).first()];
                case 2:
                    row = _a.sent();
                    return [2 /*return*/, row ? mapNotificationMessageRow(row) : null];
            }
        });
    });
}
function createNotificationMessage(payload) {
    return __awaiter(this, void 0, void 0, function () {
        var parsedPayload, insertQuery, insertResult, _a, insertedId, row;
        return __generator(this, function (_b) {
            switch (_b.label) {
                case 0: return [4 /*yield*/, ensureNotificationMessagesTable()];
                case 1:
                    _b.sent();
                    parsedPayload = exports.notificationMessagePayloadSchema.parse(payload);
                    insertQuery = (0, client_js_1.db)(exports.NOTIFICATION_MESSAGE_TABLE).insert({
                        title: parsedPayload.title,
                        body: parsedPayload.body,
                        category: parsedPayload.category,
                        source: parsedPayload.source,
                        priority: parsedPayload.priority,
                        metadata_json: parsedPayload.metadata,
                        is_read: false,
                        read_at: null,
                        created_at: client_js_1.db.fn.now(),
                        updated_at: client_js_1.db.fn.now(),
                    });
                    if (!supportsReturning()) return [3 /*break*/, 3];
                    return [4 /*yield*/, insertQuery.returning('id')];
                case 2:
                    _a = _b.sent();
                    return [3 /*break*/, 5];
                case 3: return [4 /*yield*/, insertQuery];
                case 4:
                    _a = _b.sent();
                    _b.label = 5;
                case 5:
                    insertResult = _a;
                    insertedId = resolveInsertedId(insertResult);
                    if (!insertedId) {
                        throw new Error('알림 메시지 저장 후 ID를 확인하지 못했습니다.');
                    }
                    return [4 /*yield*/, (0, client_js_1.db)(exports.NOTIFICATION_MESSAGE_TABLE).where({ id: insertedId }).first()];
                case 6:
                    row = _b.sent();
                    if (!row) {
                        throw new Error('저장된 알림 메시지를 다시 불러오지 못했습니다.');
                    }
                    return [2 /*return*/, mapNotificationMessageRow(row)];
            }
        });
    });
}
function selectNotificationMessageIdsToDelete(items, keepLatestCount) {
    if (keepLatestCount === void 0) { keepLatestCount = 1; }
    var normalizedKeepLatestCount = Number.isInteger(keepLatestCount) && keepLatestCount > 0 ? keepLatestCount : 1;
    return items
        .slice()
        .sort(function (left, right) {
        var leftCreatedAt = Date.parse(left.createdAt !== null && left.createdAt !== void 0 ? left.createdAt : '');
        var rightCreatedAt = Date.parse(right.createdAt !== null && right.createdAt !== void 0 ? right.createdAt : '');
        if (Number.isFinite(leftCreatedAt) && Number.isFinite(rightCreatedAt) && leftCreatedAt !== rightCreatedAt) {
            return rightCreatedAt - leftCreatedAt;
        }
        return right.id - left.id;
    })
        .slice(normalizedKeepLatestCount)
        .map(function (item) { return item.id; });
}
function deleteOlderNotificationMessagesBySource(source, keepLatestCount) {
    if (keepLatestCount === void 0) { keepLatestCount = 1; }
    return __awaiter(this, void 0, void 0, function () {
        var normalizedSource, rows, idsToDelete;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, ensureNotificationMessagesTable()];
                case 1:
                    _a.sent();
                    normalizedSource = source.trim();
                    if (!normalizedSource) {
                        return [2 /*return*/, 0];
                    }
                    return [4 /*yield*/, (0, client_js_1.db)(exports.NOTIFICATION_MESSAGE_TABLE)
                            .select('id', 'created_at')
                            .where({ source: normalizedSource })];
                case 2:
                    rows = _a.sent();
                    idsToDelete = selectNotificationMessageIdsToDelete(rows.map(function (row) { return ({
                        id: Number(row.id !== null && row.id !== void 0 ? row.id : 0),
                        createdAt: row.created_at === null || row.created_at === undefined ? null : String(row.created_at),
                    }); }), keepLatestCount).filter(function (id) { return Number.isInteger(id) && id > 0; });
                    if (idsToDelete.length === 0) {
                        return [2 /*return*/, 0];
                    }
                    return [2 /*return*/, (0, client_js_1.db)(exports.NOTIFICATION_MESSAGE_TABLE).whereIn('id', idsToDelete).del()];
            }
        });
    });
}
function updateNotificationMessageReadState(id, payload) {
    return __awaiter(this, void 0, void 0, function () {
        var parsedPayload, updatedCount, row;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, ensureNotificationMessagesTable()];
                case 1:
                    _a.sent();
                    parsedPayload = exports.notificationMessageReadPayloadSchema.parse(payload);
                    return [4 /*yield*/, (0, client_js_1.db)(exports.NOTIFICATION_MESSAGE_TABLE)
                            .where({ id: id })
                            .update({
                            is_read: parsedPayload.read,
                            read_at: parsedPayload.read ? client_js_1.db.fn.now() : null,
                            updated_at: client_js_1.db.fn.now(),
                        })];
                case 2:
                    updatedCount = _a.sent();
                    if (!updatedCount) {
                        return [2 /*return*/, null];
                    }
                    return [4 /*yield*/, (0, client_js_1.db)(exports.NOTIFICATION_MESSAGE_TABLE).where({ id: id }).first()];
                case 3:
                    row = _a.sent();
                    return [2 /*return*/, row ? mapNotificationMessageRow(row) : null];
            }
        });
    });
}
function deleteNotificationMessage(id) {
    return __awaiter(this, void 0, void 0, function () {
        var deletedCount;
        return __generator(this, function (_a) {
            switch (_a.label) {
                case 0: return [4 /*yield*/, ensureNotificationMessagesTable()];
                case 1:
                    _a.sent();
                    return [4 /*yield*/, (0, client_js_1.db)(exports.NOTIFICATION_MESSAGE_TABLE).where({ id: id }).del()];
                case 2:
                    deletedCount = _a.sent();
                    return [2 /*return*/, deletedCount > 0];
            }
        });
    });
}
