"use strict";
// =============================================================================
// Pure Agent - Feishu/Lark Platform Adapter
// Ported from Hermes gateway/platforms/feishu.py
// Uses Feishu Open API (webhook receive + REST send)
// =============================================================================
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.FeishuAdapter = void 0;
const crypto = __importStar(require("crypto"));
const express_1 = require("express");
const config_1 = require("../config");
const base_1 = require("./base");
const logger_1 = require("../../logger");
const platform_registry_1 = require("../platform-registry");
// ---------------------------------------------------------------------------
// FeishuAdapter
// ---------------------------------------------------------------------------
class FeishuAdapter extends base_1.BasePlatformAdapter {
    _appId;
    _appSecret;
    _encryptKey;
    _verificationToken;
    _domain;
    _router = null;
    _tenantToken = null;
    _botOpenId;
    _requireMention;
    constructor(config) {
        super(config_1.Platform.FEISHU, config);
        this._appId = config.extra?.app_id || process.env.FEISHU_APP_ID || '';
        this._appSecret = config.extra?.app_secret || process.env.FEISHU_APP_SECRET || '';
        this._encryptKey = config.extra?.encrypt_key || process.env.FEISHU_ENCRYPT_KEY || '';
        this._verificationToken = config.extra?.verification_token || process.env.FEISHU_VERIFICATION_TOKEN || '';
        this._domain = config.extra?.domain_name || process.env.FEISHU_DOMAIN || 'feishu';
        this._botOpenId = config.extra?.bot_open_id || process.env.FEISHU_BOT_OPEN_ID || '';
        this._requireMention = config.extra?.require_mention !== false;
    }
    // -------------------------------------------------------------------------
    // Connection
    // -------------------------------------------------------------------------
    async connect() {
        if (!this._appId || !this._appSecret) {
            (0, logger_1.getLogger)().error('Feishu: missing app_id or app_secret');
            return false;
        }
        // Test tenant token
        try {
            await this._getTenantToken();
        }
        catch (err) {
            (0, logger_1.getLogger)().error(`Feishu: auth failed: ${err.message}`);
            return false;
        }
        this._connected = true;
        (0, logger_1.getLogger)().info(`Feishu: connected (app: ${this._appId})`);
        return true;
    }
    async disconnect() {
        this._connected = false;
        (0, logger_1.getLogger)().info('Feishu: disconnected');
    }
    // -------------------------------------------------------------------------
    // Webhook router (for Express integration)
    // -------------------------------------------------------------------------
    getRouter() {
        if (!this._router) {
            this._router = (0, express_1.Router)();
            this._setupWebhookRoutes();
        }
        return this._router;
    }
    _setupWebhookRoutes() {
        const router = this._router;
        router.post('/webhook', async (req, res) => {
            try {
                // Signature verification
                if (this._encryptKey) {
                    const timestamp = req.headers['x-lark-request-timestamp'];
                    const nonce = req.headers['x-lark-request-nonce'];
                    const signature = req.headers['x-lark-request-signature'];
                    const bodyStr = JSON.stringify(req.body);
                    if (timestamp && nonce && signature) {
                        const expected = crypto
                            .createHash('sha256')
                            .update(timestamp + nonce + this._encryptKey + bodyStr)
                            .digest('hex');
                        if (signature !== expected) {
                            return res.status(403).json({ error: 'Invalid signature' });
                        }
                    }
                }
                const body = req.body;
                // URL verification challenge
                if (body.type === 'url_verification') {
                    return res.json({ challenge: body.challenge });
                }
                // Event callback
                if (body.header?.event_type === 'im.message.receive_v1') {
                    const event = body.event;
                    if (event?.message) {
                        await this._processMessage(event.message, event.sender);
                    }
                }
                res.json({ ok: true });
            }
            catch (err) {
                (0, logger_1.getLogger)().error(`Feishu webhook error: ${err.message}`);
                res.status(500).json({ error: err.message });
            }
        });
    }
    // -------------------------------------------------------------------------
    // Message processing
    // -------------------------------------------------------------------------
    async _processMessage(message, sender) {
        const msgType = message.message_type;
        let text = '';
        switch (msgType) {
            case 'text': {
                const content = JSON.parse(message.content || '{}');
                text = content.text || '';
                // Strip @mention
                if (this._botOpenId) {
                    text = text.replace(new RegExp(`@_user_${this._botOpenId}`, 'g'), '').trim();
                }
                break;
            }
            case 'post': {
                // Rich text - extract text content
                const content = JSON.parse(message.content || '{}');
                text = this._extractPostText(content);
                break;
            }
            case 'image':
                text = '[Image message]';
                break;
            case 'file':
            case 'audio':
            case 'media':
                text = '[File attachment]';
                break;
            default:
                text = `[${msgType} message]`;
        }
        if (!text)
            return;
        const chatType = message.chat_type === 'p2p' ? 'dm' : 'group';
        const event = {
            text,
            messageType: base_1.MessageType.TEXT,
            platform: config_1.Platform.FEISHU,
            chatId: message.chat_id,
            chatType,
            userId: sender?.sender_id?.open_id,
            userName: sender?.sender_id?.open_id,
            messageId: message.message_id,
            timestamp: message.create_time ? parseInt(message.create_time) : Date.now(),
            rawMessage: { message, sender },
        };
        await this.handleMessage(event);
    }
    _extractPostText(content) {
        const parts = [];
        const title = content.title;
        if (title)
            parts.push(title);
        const zhContent = content.content || content.zh_cn?.content || content.en_us?.content || [];
        if (Array.isArray(zhContent)) {
            for (const paragraph of zhContent) {
                if (Array.isArray(paragraph)) {
                    for (const element of paragraph) {
                        if (element.tag === 'text')
                            parts.push(element.text || '');
                        else if (element.tag === 'a')
                            parts.push(element.text || element.href || '');
                        else if (element.tag === 'at')
                            parts.push(`@${element.user_name || element.user_id || ''}`);
                    }
                }
            }
        }
        return parts.join(' ').trim();
    }
    // -------------------------------------------------------------------------
    // Send message
    // -------------------------------------------------------------------------
    async send(chatId, content, replyTo) {
        const token = await this._getTenantToken();
        const baseUrl = this._domain === 'lark' ? 'https://open.larksuite.com' : 'https://open.feishu.cn';
        // Truncate
        const truncated = content.length > 8000 ? content.slice(0, 7950) + '\n\n... [truncated]' : content;
        // Build payload - prefer text type for simplicity
        const payload = {
            receive_id: chatId,
            msg_type: 'text',
            content: JSON.stringify({ text: truncated }),
        };
        const url = replyTo
            ? `${baseUrl}/open-apis/im/v1/messages/${replyTo}/reply`
            : `${baseUrl}/open-apis/im/v1/messages`;
        const params = replyTo ? {} : { receive_id_type: 'chat_id' };
        try {
            const fullUrl = new URL(url);
            for (const [k, v] of Object.entries(params))
                fullUrl.searchParams.set(k, v);
            const response = await fetch(fullUrl.toString(), {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${token}`,
                },
                body: JSON.stringify(payload),
                signal: AbortSignal.timeout(30000),
            });
            const result = await response.json();
            if (result.code === 0) {
                return { success: true, messageId: result.data?.message_id };
            }
            return { success: false, error: result.msg || `Feishu API error: ${result.code}` };
        }
        catch (err) {
            return { success: false, error: err.message };
        }
    }
    async editMessage(chatId, messageId, content) {
        const token = await this._getTenantToken();
        const baseUrl = this._domain === 'lark' ? 'https://open.larksuite.com' : 'https://open.feishu.cn';
        try {
            const response = await fetch(`${baseUrl}/open-apis/im/v1/messages/${messageId}`, {
                method: 'PATCH',
                headers: {
                    'Content-Type': 'application/json',
                    'Authorization': `Bearer ${token}`,
                },
                body: JSON.stringify({
                    msg_type: 'text',
                    content: JSON.stringify({ text: content.slice(0, 8000) }),
                }),
                signal: AbortSignal.timeout(15000),
            });
            const result = await response.json();
            if (result.code === 0)
                return { success: true, messageId };
            return { success: false, error: result.msg };
        }
        catch (err) {
            return { success: false, error: err.message };
        }
    }
    async getChatInfo(chatId) {
        return { name: `feishu:${chatId}`, type: 'group' };
    }
    // -------------------------------------------------------------------------
    // Tenant token management
    // -------------------------------------------------------------------------
    async _getTenantToken() {
        if (this._tenantToken && Date.now() < this._tenantToken.expiresAt) {
            return this._tenantToken.token;
        }
        const baseUrl = this._domain === 'lark' ? 'https://open.larksuite.com' : 'https://open.feishu.cn';
        const response = await fetch(`${baseUrl}/open-apis/auth/v3/tenant_access_token/internal`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ app_id: this._appId, app_secret: this._appSecret }),
            signal: AbortSignal.timeout(10000),
        });
        const result = await response.json();
        if (result.code !== 0)
            throw new Error(`Feishu auth error: ${result.msg}`);
        this._tenantToken = {
            token: result.tenant_access_token,
            expiresAt: Date.now() + (result.expire - 300) * 1000, // 5min safety margin
        };
        return this._tenantToken.token;
    }
}
exports.FeishuAdapter = FeishuAdapter;
// Auto-register
platform_registry_1.platformRegistry.register({
    name: 'feishu',
    label: 'Feishu / Lark',
    factory: (config) => new FeishuAdapter(config),
    checkFn: () => !!(process.env.FEISHU_APP_ID || process.env.FEISHU_APP_SECRET),
    requiredEnv: ['FEISHU_APP_ID', 'FEISHU_APP_SECRET'],
    installHint: 'Set FEISHU_APP_ID and FEISHU_APP_SECRET environment variables',
    emoji: '🪶',
    maxMessageLength: 8000,
});
//# sourceMappingURL=feishu.js.map