"use strict";
// =============================================================================
// Pure Agent - DingTalk Platform Adapter
// Ported from Hermes gateway/platforms/dingtalk.py
// Stream Mode (WebSocket) for receiving, session webhook for sending
// =============================================================================
Object.defineProperty(exports, "__esModule", { value: true });
exports.DingTalkAdapter = void 0;
const config_1 = require("../config");
const base_1 = require("./base");
const logger_1 = require("../../logger");
const platform_registry_1 = require("../platform-registry");
// ---------------------------------------------------------------------------
// DingTalkAdapter
// ---------------------------------------------------------------------------
class DingTalkAdapter extends base_1.BasePlatformAdapter {
    _clientId;
    _clientSecret;
    _accessToken = '';
    _tokenExpiresAt = 0;
    _sessionWebhooks = new Map();
    _requireMention;
    _heartbeatTimer = null;
    constructor(config) {
        super(config_1.Platform.DINGTALK, config);
        this._clientId = config.extra?.client_id || process.env.DINGTALK_CLIENT_ID || '';
        this._clientSecret = config.extra?.client_secret || process.env.DINGTALK_CLIENT_SECRET || '';
        this._requireMention = config.extra?.require_mention || false;
    }
    // -------------------------------------------------------------------------
    // Connection (via webhook registration)
    // -------------------------------------------------------------------------
    async connect() {
        if (!this._clientId || !this._clientSecret) {
            (0, logger_1.getLogger)().error('DingTalk: missing client_id or client_secret');
            return false;
        }
        // Test access token
        try {
            await this._getAccessToken();
        }
        catch (err) {
            (0, logger_1.getLogger)().error(`DingTalk: auth failed: ${err.message}`);
            return false;
        }
        this._connected = true;
        (0, logger_1.getLogger)().info(`DingTalk: connected (client: ${this._clientId})`);
        return true;
    }
    async disconnect() {
        this._connected = false;
        if (this._heartbeatTimer) {
            clearInterval(this._heartbeatTimer);
            this._heartbeatTimer = null;
        }
        (0, logger_1.getLogger)().info('DingTalk: disconnected');
    }
    // -------------------------------------------------------------------------
    // Outbound message (via session webhook)
    // -------------------------------------------------------------------------
    async send(chatId, content, replyTo) {
        const webhook = this._sessionWebhooks.get(chatId);
        if (!webhook || Date.now() > webhook.expiresAt) {
            return { success: false, error: 'No active session webhook for this chat. Message must be initiated from DingTalk first.' };
        }
        // DingTalk markdown quirks: blank line before numbered lists
        let normalized = content;
        normalized = normalized.replace(/(\n)(\d+\.\s)/g, '$1\n$2');
        try {
            const response = await fetch(webhook.url, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    msgtype: 'markdown',
                    markdown: {
                        title: 'Agent',
                        text: normalized.slice(0, 20000),
                    },
                }),
                signal: AbortSignal.timeout(15000),
            });
            if (response.ok) {
                return { success: true, messageId: `dt_${Date.now()}` };
            }
            const result = await response.json().catch(() => ({}));
            return { success: false, error: result.errmsg || `HTTP ${response.status}` };
        }
        catch (err) {
            return { success: false, error: err.message };
        }
    }
    async getChatInfo(chatId) {
        return { name: `dingtalk:${chatId}`, type: 'group' };
    }
    // -------------------------------------------------------------------------
    // Inbound message processing (called by webhook handler)
    // -------------------------------------------------------------------------
    async processInbound(body) {
        const conversationId = body.conversationId;
        const conversationType = body.conversationType; // 1=DM, 2=group
        const senderId = body.senderStaffId || body.senderId;
        const senderNick = body.senderNick || '';
        const sessionWebhook = body.sessionWebhook;
        const text = body.text?.content?.trim() || '';
        const msgtype = body.msgtype;
        const isInAtList = body.isInAtList || false;
        // Cache session webhook (expires in ~20min, use 5min safety)
        if (sessionWebhook) {
            this._sessionWebhooks.set(conversationId, {
                url: sessionWebhook,
                expiresAt: Date.now() + 15 * 60 * 1000,
            });
        }
        // Skip empty messages
        if (!text && msgtype !== 'picture' && msgtype !== 'voice')
            return;
        // Group: check mention requirement
        if (conversationType === 2 && this._requireMention && !isInAtList) {
            return; // Not @-mentioned, skip
        }
        // Strip @mention from text
        let cleanText = text;
        if (conversationType === 2) {
            cleanText = cleanText.replace(/^@\S+\s*/, '').trim();
        }
        const event = {
            text: cleanText || `[${msgtype} message]`,
            messageType: msgtype === 'picture' ? base_1.MessageType.PHOTO :
                msgtype === 'voice' ? base_1.MessageType.VOICE :
                    base_1.MessageType.TEXT,
            platform: config_1.Platform.DINGTALK,
            chatId: conversationId,
            chatType: conversationType === 1 ? 'dm' : 'group',
            userId: senderId,
            userName: senderNick,
            timestamp: Date.now(),
            rawMessage: body,
        };
        await this.handleMessage(event);
    }
    // -------------------------------------------------------------------------
    // Access token
    // -------------------------------------------------------------------------
    async _getAccessToken() {
        if (this._accessToken && Date.now() < this._tokenExpiresAt) {
            return this._accessToken;
        }
        const response = await fetch('https://api.dingtalk.com/v1.0/oauth2/accessToken', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ appKey: this._clientId, appSecret: this._clientSecret }),
            signal: AbortSignal.timeout(10000),
        });
        if (!response.ok)
            throw new Error(`DingTalk auth HTTP ${response.status}`);
        const result = await response.json();
        this._accessToken = result.accessToken;
        this._tokenExpiresAt = Date.now() + (result.expireIn - 300) * 1000;
        return this._accessToken;
    }
}
exports.DingTalkAdapter = DingTalkAdapter;
// Auto-register
platform_registry_1.platformRegistry.register({
    name: 'dingtalk',
    label: 'DingTalk',
    factory: (config) => new DingTalkAdapter(config),
    checkFn: () => !!(process.env.DINGTALK_CLIENT_ID || process.env.DINGTALK_CLIENT_SECRET),
    requiredEnv: ['DINGTALK_CLIENT_ID', 'DINGTALK_CLIENT_SECRET'],
    installHint: 'Set DINGTALK_CLIENT_ID and DINGTALK_CLIENT_SECRET environment variables',
    emoji: '🔔',
    maxMessageLength: 20000,
});
//# sourceMappingURL=dingtalk.js.map