/**
 * CardKit v2 API wrapper for Feishu.
 *
 * Encapsulates the five CardKit API calls needed for streaming cards:
 * - createCardEntity: Create a card entity
 * - sendCardByCardId: Send card by referencing card_id
 * - streamCardContent: Stream content to a card element
 * - updateCardKitCard: Replace full card content
 * - setCardStreamingMode: Toggle streaming mode
 *
 * Adapted from openclaw-lark's card/cardkit.ts.
 */

import type * as Lark from '@larksuiteoapi/node-sdk'
import { CardKitResponse, CardKitApiError } from './card-error'

// ---------------------------------------------------------------------------
// CardKit API functions
// ---------------------------------------------------------------------------

/**
 * Create a card entity via the CardKit API.
 * Returns the card_id, or null on failure.
 */
export async function createCardEntity(params: {
  client: Lark.Client
  card: Record<string, unknown>
}): Promise<string | null> {
  const { client, card } = params

  try {
    const response = (await client.cardkit.v1.card.create({
      data: {
        type: 'card_json',
        data: JSON.stringify(card),
      },
    })) as CardKitResponse

    const code = response?.code ?? (response as Record<string, unknown>)?.code as number | undefined
    if (code && code !== 0) {
      const msg = response?.msg ?? String(response)
      console.warn(`[CardKit] create FAILED: code=${code}, msg=${msg}`)
      throw new CardKitApiError({ api: 'card.create', code, msg: String(msg), context: 'createCardEntity' })
    }

    const cardId =
      ((response?.data as Record<string, unknown>)?.card_id as string) ??
      ((response as Record<string, unknown>)?.card_id as string) ??
      null
    console.log(`[CardKit] Card entity created: cardId=${cardId}`)
    return cardId
  } catch (err) {
    if (err instanceof CardKitApiError) throw err
    console.warn('[CardKit] createCardEntity error:', err instanceof Error ? err.message : err)
    return null
  }
}

/**
 * Send an interactive card message by referencing a CardKit card_id.
 * The content format is: {type: "card", data: {card_id: "xxx"}}
 */
export async function sendCardByCardId(params: {
  client: Lark.Client
  chatId: string
  cardId: string
}): Promise<{ messageId: string; chatId: string } | undefined> {
  const { client, chatId, cardId } = params

  const contentPayload = JSON.stringify({
    type: 'card',
    data: { card_id: cardId },
  })

  try {
    const response = await client.im.message.create({
      params: { receive_id_type: 'chat_id' as const },
      data: {
        receive_id: chatId,
        msg_type: 'interactive',
        content: contentPayload,
      },
    })

    const messageId = response?.data?.message_id ?? ''
    const chat = response?.data?.chat_id ?? ''
    console.log(`[CardKit] Card sent by cardId: messageId=${messageId}`)
    return { messageId, chatId: chat }
  } catch (err) {
    console.error('[CardKit] sendCardByCardId error:', err instanceof Error ? err.message : err)
    throw err
  }
}

/**
 * Stream text content to a specific card element using the CardKit API.
 * The content is the full cumulative text (not a delta).
 * The sequence number must be monotonically increasing.
 */
export async function streamCardContent(params: {
  client: Lark.Client
  cardId: string
  elementId: string
  content: string
  sequence: number
}): Promise<void> {
  const { client, cardId, elementId, content, sequence } = params

  const resp = (await client.cardkit.v1.cardElement.content({
    data: { content, sequence },
    path: { card_id: cardId, element_id: elementId },
  })) as CardKitResponse

  const code = resp?.code
  if (code && Number(code) !== 0) {
    const msg = resp?.msg ?? ''
    console.warn(`[CardKit] streamCardContent FAILED: code=${code}, msg=${msg}, seq=${sequence}`)
    throw new CardKitApiError({
      api: 'cardElement.content',
      code: Number(code),
      msg: String(msg),
      context: `cardId=${cardId}, seq=${sequence}`,
    })
  }
}

/**
 * Replace the full card content of a CardKit entity.
 * Used for the final "complete" state update after streaming finishes.
 */
export async function updateCardKitCard(params: {
  client: Lark.Client
  cardId: string
  card: Record<string, unknown>
  sequence: number
}): Promise<void> {
  const { client, cardId, card, sequence } = params

  const resp = (await client.cardkit.v1.card.update({
    data: {
      card: { type: 'card_json', data: JSON.stringify(card) },
      sequence,
    },
    path: { card_id: cardId },
  })) as CardKitResponse

  const code = resp?.code
  if (code && Number(code) !== 0) {
    const msg = resp?.msg ?? ''
    console.warn(`[CardKit] updateCardKitCard FAILED: code=${code}, msg=${msg}, seq=${sequence}`)
    throw new CardKitApiError({
      api: 'card.update',
      code: Number(code),
      msg: String(msg),
      context: `cardId=${cardId}, seq=${sequence}`,
    })
  }
}

/**
 * Toggle streaming mode on a CardKit card.
 * Must be called after streaming is complete to restore normal card behavior.
 */
export async function setCardStreamingMode(params: {
  client: Lark.Client
  cardId: string
  streamingMode: boolean
  sequence: number
}): Promise<void> {
  const { client, cardId, streamingMode, sequence } = params

  const resp = (await client.cardkit.v1.card.settings({
    data: {
      settings: JSON.stringify({ streaming_mode: streamingMode }),
      sequence,
    },
    path: { card_id: cardId },
  })) as CardKitResponse

  const code = resp?.code
  if (code && Number(code) !== 0) {
    const msg = resp?.msg ?? ''
    console.warn(`[CardKit] setCardStreamingMode FAILED: code=${code}, streamingMode=${streamingMode}`)
    throw new CardKitApiError({
      api: 'card.settings',
      code: Number(code),
      msg: String(msg),
      context: `cardId=${cardId}, streamingMode=${streamingMode}, seq=${sequence}`,
    })
  }
  console.log(`[CardKit] Streaming mode set: ${streamingMode}, cardId=${cardId}`)
}