"use strict";
/**
 * Copyright (c) 2026 ByteDance Ltd. and/or its affiliates
 * SPDX-License-Identifier: MIT
 *
 * CardKit streaming APIs for Lark/Feishu.
 */
import { LarkClient } from '../core/lark-client';
import { larkLogger } from '../core/lark-logger';
import { normalizeFeishuTarget, normalizeMessageId, resolveReceiveIdType } from '../core/targets';
import { runWithMessageUnavailableGuard } from '../core/message-unavailable';
const log = larkLogger('card/cardkit');
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/**
 * 记录 CardKit API 响应日志，检测错误码并抛出异常。
 *
 * 默认 fail-fast：body-level 非零 code 视为业务错误，立即抛出，
 * 由调用方（streaming-card-controller 等）统一走 catch → guard 处理。
 */
function logCardKitResponse(params) {
    const { resp, api, context } = params;
    const { code, msg } = resp;
    log.info(`cardkit ${api} response`, { code, msg, context });
    if (code && code !== 0) {
        log.warn(`cardkit ${api} FAILED`, { code, msg, context, fullResponse: resp });
        throw new Error(`cardkit ${api} FAILED: code=${code}, msg=${msg ?? ''}, ${context}`);
    }
}
// ---------------------------------------------------------------------------
// CardKit streaming APIs
// ---------------------------------------------------------------------------
/**
 * Create a card entity via the CardKit API.
 *
 * Returns the card_id directly, bypassing the idConvert step.
 * The card can then be sent via IM API and streamed via CardKit.
 */
export async function createCardEntity(params) {
    const { cfg, card, accountId } = params;
    const client = LarkClient.fromCfg(cfg, accountId).sdk;
    // SDK 返回类型不完整，运行时包含 code/msg/data 字段
    const response = (await client.cardkit.v1.card.create({
        data: {
            type: 'card_json',
            data: JSON.stringify(card),
        },
    }));
    // 兼容不同 SDK 包装层：优先 data.card_id，回退顶层 card_id
    const cardId = (response.data?.card_id ?? response.card_id) ?? null;
    logCardKitResponse({ resp: response, api: 'card.create', context: `cardId=${cardId}` });
    return cardId;
}
/**
 * Stream text content to a specific card element using the CardKit API.
 *
 * The card automatically diffs the new content against the previous
 * content and renders incremental changes with a typewriter animation.
 *
 * @param params.cardId    - CardKit card ID (from `convertMessageToCardId`).
 * @param params.elementId - The element ID to update (e.g. `STREAMING_ELEMENT_ID`).
 * @param params.content   - The full cumulative text (not a delta).
 * @param params.sequence  - Monotonically increasing sequence number.
 */
export async function streamCardContent(params) {
    const { cfg, cardId, elementId, content, sequence, accountId } = params;
    const client = LarkClient.fromCfg(cfg, accountId).sdk;
    // SDK 返回类型不完整，运行时包含 code/msg 字段
    const resp = (await client.cardkit.v1.cardElement.content({
        data: { content, sequence },
        path: { card_id: cardId, element_id: elementId },
    }));
    logCardKitResponse({
        resp,
        api: 'cardElement.content',
        context: `seq=${sequence}, contentLen=${content.length}`,
    });
}
/**
 * Fully replace a card using the CardKit API.
 *
 * Used for the final "complete" state update (with action buttons, green
 * header, etc.) after streaming finishes.
 *
 * @param params.cardId   - CardKit card ID.
 * @param params.card     - The new card JSON content.
 * @param params.sequence - Monotonically increasing sequence number.
 */
export async function updateCardKitCard(params) {
    const { cfg, cardId, card, sequence, accountId } = params;
    const client = LarkClient.fromCfg(cfg, accountId).sdk;
    // SDK 返回类型不完整，运行时包含 code/msg 字段
    const resp = (await client.cardkit.v1.card.update({
        data: {
            card: { type: 'card_json', data: JSON.stringify(card) },
            sequence,
        },
        path: { card_id: cardId },
    }));
    logCardKitResponse({
        resp,
        api: 'card.update',
        context: `seq=${sequence}, cardId=${cardId}`,
    });
}
export async function updateCardKitCardForAuth(params) {
    return updateCardKitCard(params);
}
/**
 * Send an interactive card message by referencing a CardKit card_id.
 *
 * The content format is: {"type":"card","data":{"card_id":"xxx"}}
 * This links the IM message to the CardKit card entity, enabling
 * streaming updates via cardElement.content().
 */
export async function sendCardByCardId(params) {
    const { cfg, to, cardId, replyToMessageId, replyInThread, accountId } = params;
    const client = LarkClient.fromCfg(cfg, accountId).sdk;
    const contentPayload = JSON.stringify({
        type: 'card',
        data: { card_id: cardId },
    });
    if (replyToMessageId) {
        // 规范化 message_id，处理合成 ID（如 "om_xxx:auth-complete"）
        const normalizedId = normalizeMessageId(replyToMessageId);
        const response = await runWithMessageUnavailableGuard({
            messageId: normalizedId,
            operation: 'im.message.reply(interactive.cardkit)',
            fn: () => client.im.message.reply({
                path: { message_id: normalizedId },
                data: { content: contentPayload, msg_type: 'interactive', reply_in_thread: replyInThread },
            }),
        });
        return {
            messageId: response?.data?.message_id ?? '',
            chatId: response?.data?.chat_id ?? '',
        };
    }
    const target = normalizeFeishuTarget(to);
    if (!target) {
        throw new Error(`[feishu-send] Invalid target: "${to}"`);
    }
    const receiveIdType = resolveReceiveIdType(target);
    const response = await client.im.message.create({
        // SDK 类型将 receive_id_type 限定为字面量联合，但运行时接受动态值
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        params: { receive_id_type: receiveIdType },
        data: {
            receive_id: target,
            msg_type: 'interactive',
            content: contentPayload,
        },
    });
    return {
        messageId: response?.data?.message_id ?? '',
        chatId: response?.data?.chat_id ?? '',
    };
}
/**
 * Close (or open) the streaming mode on a CardKit card.
 *
 * Must be called after streaming is complete to restore normal card
 * behaviour (forwarding, interaction callbacks, etc.).
 */
export async function setCardStreamingMode(params) {
    const { cfg, cardId, streamingMode, sequence, accountId } = params;
    const client = LarkClient.fromCfg(cfg, accountId).sdk;
    // SDK 返回类型不完整，运行时包含 code/msg 字段
    const resp = (await client.cardkit.v1.card.settings({
        data: {
            settings: JSON.stringify({ streaming_mode: streamingMode }),
            sequence,
        },
        path: { card_id: cardId },
    }));
    logCardKitResponse({
        resp,
        api: 'card.settings',
        context: `seq=${sequence}, streaming_mode=${streamingMode}`,
    });
}
