"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildAppReminderOwnerRegistryName = buildAppReminderOwnerRegistryName;
exports.ensureAppReminderOwnersRegistered = ensureAppReminderOwnersRegistered;
exports.unregisterAppReminderOwnersForApps = unregisterAppReminderOwnersForApps;
exports.applyAppReminderRequests = applyAppReminderRequests;
const evt_registry_1 = require("../evt-registry");
const log_1 = require("../log");
const driver_messages_1 = require("../runtime/driver-messages");
const work_language_1 = require("../runtime/work-language");
const shared_reminders_1 = require("../shared-reminders");
const tool_1 = require("../tool");
const registry_1 = require("./registry");
const APP_REMINDER_META_KEYS = new Set([
    'kind',
    'appId',
    'ownerRef',
    'manager',
    'update',
    'delete',
    'ownerMeta',
]);
const appReminderDescriptors = new Map();
function isRecord(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function isJsonRecord(value) {
    return typeof value === 'object' && value !== null && !Array.isArray(value);
}
function isAppReminderMeta(value) {
    if (!isRecord(value))
        return false;
    if (value['kind'] !== 'app_reminder_owner')
        return false;
    if (typeof value['appId'] !== 'string' || value['appId'].trim() === '')
        return false;
    if (typeof value['ownerRef'] !== 'string' || value['ownerRef'].trim() === '')
        return false;
    const manager = value['manager'];
    if (!isRecord(manager))
        return false;
    if (typeof manager['tool'] !== 'string' || manager['tool'].trim() === '')
        return false;
    const update = value['update'];
    if (update !== undefined &&
        (!isRecord(update) ||
            typeof update['altInstruction'] !== 'string' ||
            update['altInstruction'].trim() === '')) {
        return false;
    }
    const del = value['delete'];
    if (del !== undefined &&
        (!isRecord(del) ||
            typeof del['altInstruction'] !== 'string' ||
            del['altInstruction'].trim() === '')) {
        return false;
    }
    return true;
}
function normalizeInsertPosition(remindersLength, position) {
    if (position === undefined)
        return remindersLength;
    if (!Number.isFinite(position))
        return remindersLength;
    const normalized = Math.floor(position);
    if (normalized <= 0)
        return 0;
    if (normalized >= remindersLength)
        return remindersLength;
    return normalized;
}
function toManagerTool(owner) {
    const tool = owner.manager?.tool?.trim();
    if (tool && tool.length > 0)
        return tool;
    return owner.ref;
}
function toUpdateAltInstruction(owner) {
    const altInstruction = owner.update?.altInstruction?.trim();
    return altInstruction && altInstruction.length > 0 ? altInstruction : undefined;
}
function toDeleteAltInstruction(owner) {
    const altInstruction = owner.delete?.altInstruction?.trim();
    return altInstruction && altInstruction.length > 0 ? altInstruction : undefined;
}
function buildDescriptor(params) {
    return {
        appId: params.appId,
        ownerRef: params.owner.ref,
        registryName: buildAppReminderOwnerRegistryName(params.appId, params.owner.ref),
        managerTool: toManagerTool(params.owner),
        updateAltInstruction: toUpdateAltInstruction(params.owner),
        deleteAltInstruction: toDeleteAltInstruction(params.owner),
    };
}
function buildAppReminderMeta(descriptor, ownerMeta) {
    const baseMeta = {
        kind: 'app_reminder_owner',
        appId: descriptor.appId,
        ownerRef: descriptor.ownerRef,
        manager: {
            tool: descriptor.managerTool,
        },
        ...(descriptor.updateAltInstruction === undefined
            ? {}
            : {
                update: {
                    altInstruction: descriptor.updateAltInstruction,
                },
            }),
        ...(descriptor.deleteAltInstruction === undefined
            ? {}
            : {
                delete: {
                    altInstruction: descriptor.deleteAltInstruction,
                },
            }),
    };
    if (isJsonRecord(ownerMeta)) {
        return { ...ownerMeta, ...baseMeta };
    }
    if (ownerMeta !== undefined) {
        return { ...baseMeta, ownerMeta };
    }
    return baseMeta;
}
function extractOwnerMeta(meta) {
    if (!isJsonRecord(meta)) {
        return undefined;
    }
    const payloadEntries = Object.entries(meta).filter(([key]) => !APP_REMINDER_META_KEYS.has(key));
    if (payloadEntries.length > 0) {
        return Object.fromEntries(payloadEntries);
    }
    return meta['ownerMeta'];
}
function toReminderState(reminder) {
    return {
        content: reminder.content,
        meta: extractOwnerMeta(reminder.meta),
        echoback: reminder.echoback,
        renderMode: reminder.renderMode,
    };
}
function findOwnedReminderEntries(dlg, descriptor, owner) {
    const entries = [];
    for (let index = 0; index < dlg.reminders.length; index += 1) {
        const reminder = dlg.reminders[index];
        if (!reminder || reminder.owner !== owner || !isAppReminderMeta(reminder.meta)) {
            continue;
        }
        if (reminder.meta.appId !== descriptor.appId ||
            reminder.meta.ownerRef !== descriptor.ownerRef) {
            continue;
        }
        entries.push({ index, reminder });
    }
    return entries;
}
function fallbackRenderedReminder(reminder) {
    const language = (0, work_language_1.getWorkLanguage)();
    return {
        type: 'environment_msg',
        role: 'user',
        content: (0, driver_messages_1.formatReminderItemGuide)(language, reminder.id, reminder.content, {
            meta: reminder.meta,
            scope: reminder.scope,
        }),
    };
}
function formatAppReminderWrapperPrelude(language, reminderId) {
    return language === 'zh'
        ? `这是 app 自动维护的状态提醒 [${reminderId}]，不是你自己写的工作便签。${(0, driver_messages_1.formatAutoMaintainedReminderManualMirrorBan)(language)}`
        : `This is app-maintained state [${reminderId}], not a work note you wrote. ${(0, driver_messages_1.formatAutoMaintainedReminderManualMirrorBan)(language)}`;
}
function wrapAppRenderedReminder(reminderId, message, language) {
    return {
        type: 'environment_msg',
        role: 'user',
        content: `${formatAppReminderWrapperPrelude(language, reminderId)}\n\n${message.content}`,
    };
}
async function persistAndPublishReminders(dlg) {
    await dlg.dlgStore.persistReminders(dlg, dlg.reminders);
    const taskSharedReminders = await (0, shared_reminders_1.loadSharedReminders)({
        kind: 'task',
        agentId: dlg.agentId,
        taskDocPath: dlg.taskDocPath,
    });
    const runtimeReminders = await (0, shared_reminders_1.loadSharedReminders)({ kind: 'agent', agentId: dlg.agentId });
    const visibleReminders = [
        ...dlg.reminders.map((reminder) => (0, tool_1.cloneReminder)(reminder)),
        ...taskSharedReminders,
        ...runtimeReminders,
    ];
    visibleReminders.sort(tool_1.compareReminderDisplayOrder);
    const reminders = visibleReminders.map((reminder) => ({
        content: reminder.content,
        meta: (0, tool_1.serializeReminderContentMeta)(reminder),
        reminder_id: reminder.id,
        renderRevision: (0, tool_1.computeReminderRenderRevision)(reminder),
        echoback: (0, tool_1.reminderEchoBackEnabled)(reminder),
        scope: reminder.scope,
        renderMode: reminder.renderMode,
    }));
    const evt = { type: 'full_reminders_update', reminders };
    (0, evt_registry_1.postDialogEvent)(dlg, evt);
}
function createAppReminderOwner(params) {
    const { descriptor, resolveHostClient } = params;
    const owner = {
        name: descriptor.registryName,
        async updateReminder(dlg, reminder) {
            if (reminder.owner !== owner || !isAppReminderMeta(reminder.meta)) {
                return { treatment: 'keep' };
            }
            try {
                const client = await resolveHostClient();
                const result = await client.updateReminder(descriptor.appId, descriptor.ownerRef, {
                    dialogId: dlg.id.selfId,
                    reminder: toReminderState(reminder),
                });
                if (result.treatment !== 'update') {
                    return result;
                }
                return {
                    treatment: 'update',
                    updatedContent: result.updatedContent ?? reminder.content,
                    updatedMeta: result.updatedMeta !== undefined
                        ? buildAppReminderMeta(descriptor, result.updatedMeta)
                        : reminder.meta,
                };
            }
            catch (error) {
                log_1.log.warn('App reminder update failed; keeping existing reminder state', error, {
                    appId: descriptor.appId,
                    ownerRef: descriptor.ownerRef,
                    dialogId: dlg.id.selfId,
                    reminderId: reminder.id,
                });
                return { treatment: 'keep' };
            }
        },
        async renderReminder(dlg, reminder) {
            if (reminder.owner !== owner || !isAppReminderMeta(reminder.meta)) {
                return fallbackRenderedReminder(reminder);
            }
            try {
                const client = await resolveHostClient();
                const language = (0, work_language_1.getWorkLanguage)();
                const rendered = await client.renderReminder(descriptor.appId, descriptor.ownerRef, {
                    dialogId: dlg.id.selfId,
                    reminder: toReminderState(reminder),
                    reminderId: reminder.id,
                    workLanguage: language,
                });
                return wrapAppRenderedReminder(reminder.id, rendered, language);
            }
            catch (error) {
                log_1.log.warn('App reminder render failed; using generic reminder rendering', error, {
                    appId: descriptor.appId,
                    ownerRef: descriptor.ownerRef,
                    dialogId: dlg.id.selfId,
                    reminderId: reminder.id,
                });
                return fallbackRenderedReminder(reminder);
            }
        },
    };
    return owner;
}
function buildAppReminderOwnerRegistryName(appId, ownerRef) {
    return `app/${appId}/${ownerRef}`;
}
function ensureAppReminderOwnersRegistered(params) {
    for (const app of params.enabledApps) {
        const reminderOwners = app.installJson.contributes?.reminderOwners ?? [];
        const seenOwnerRefs = new Set();
        for (const owner of reminderOwners) {
            if (seenOwnerRefs.has(owner.ref)) {
                throw new Error(`App '${app.appId}' declares duplicate reminder owner ref '${owner.ref}'`);
            }
            seenOwnerRefs.add(owner.ref);
            const descriptor = buildDescriptor({ appId: app.appId, owner });
            appReminderDescriptors.set(descriptor.registryName, descriptor);
            if ((0, registry_1.getReminderOwner)(descriptor.registryName)) {
                continue;
            }
            (0, registry_1.registerReminderOwner)(createAppReminderOwner({ descriptor, resolveHostClient: params.resolveHostClient }));
        }
    }
}
function unregisterAppReminderOwnersForApps(params) {
    const appIdSet = new Set(params.appIds);
    for (const [registryName, descriptor] of appReminderDescriptors.entries()) {
        if (!appIdSet.has(descriptor.appId)) {
            continue;
        }
        (0, registry_1.unregisterReminderOwner)(registryName);
        appReminderDescriptors.delete(registryName);
    }
}
async function applyAppReminderRequests(dlg, params) {
    const client = await params.resolveHostClient();
    let changed = false;
    for (const request of params.reminderRequests) {
        const registryName = buildAppReminderOwnerRegistryName(params.appId, request.ownerRef);
        const descriptor = appReminderDescriptors.get(registryName);
        const owner = (0, registry_1.getReminderOwner)(registryName);
        if (!descriptor || !owner) {
            throw new Error(`App '${params.appId}' attempted to use unregistered reminder owner '${request.ownerRef}'`);
        }
        const ownedEntries = findOwnedReminderEntries(dlg, descriptor, owner);
        const result = await client.applyReminder(params.appId, request.ownerRef, request, {
            dialogId: dlg.id.selfId,
            ownedReminders: ownedEntries.map((entry) => toReminderState(entry.reminder)),
        });
        switch (result.treatment) {
            case 'noop':
                break;
            case 'add': {
                dlg.addReminder(result.reminder.content, owner, buildAppReminderMeta(descriptor, result.reminder.meta), normalizeInsertPosition(dlg.reminders.length, result.position), {
                    echoback: result.reminder.echoback,
                    scope: 'dialog',
                    renderMode: result.reminder.renderMode ?? 'markdown',
                });
                changed = true;
                break;
            }
            case 'update': {
                const target = ownedEntries[result.ownedIndex];
                if (!target) {
                    throw new Error(`App '${params.appId}' reminder owner '${request.ownerRef}' returned invalid ownedIndex=${result.ownedIndex}`);
                }
                dlg.updateReminder(target.index, result.reminder.content, buildAppReminderMeta(descriptor, result.reminder.meta), {
                    echoback: result.reminder.echoback,
                    // Preserve the existing render mode when the app does not explicitly override it.
                    renderMode: result.reminder.renderMode ?? target.reminder.renderMode,
                });
                changed = true;
                break;
            }
            case 'delete': {
                const target = ownedEntries[result.ownedIndex];
                if (!target) {
                    throw new Error(`App '${params.appId}' reminder owner '${request.ownerRef}' returned invalid ownedIndex=${result.ownedIndex}`);
                }
                dlg.deleteReminder(target.index);
                changed = true;
                break;
            }
            default: {
                const _exhaustive = result;
                throw new Error(`Unsupported app reminder apply result: ${String(_exhaustive)}`);
            }
        }
    }
    if (changed) {
        await persistAndPublishReminders(dlg);
    }
}
