/**
 * Twitch message actions adapter.
 *
 * Handles tool-based actions for Twitch, such as sending messages.
 */

import type { ChannelMessageActionAdapter, ChannelMessageActionContext } from "./types.js";
import { DEFAULT_ACCOUNT_ID, getAccountConfig } from "./config.js";
import { twitchOutbound } from "./outbound.js";

/**
 * Create a tool result with error content.
 */
function errorResponse(error: string) {
  return {
    content: [
      {
        type: "text" as const,
        text: JSON.stringify({ ok: false, error }),
      },
    ],
    details: { ok: false },
  };
}

/**
 * Read a string parameter from action arguments.
 *
 * @param args - Action arguments
 * @param key - Parameter key
 * @param options - Options for reading the parameter
 * @returns The parameter value or undefined if not found
 */
function readStringParam(
  args: Record<string, unknown>,
  key: string,
  options: { required?: boolean; trim?: boolean } = {},
): string | undefined {
  const value = args[key];
  if (value === undefined || value === null) {
    if (options.required) {
      throw new Error(`Missing required parameter: ${key}`);
    }
    return undefined;
  }

  // Convert value to string safely
  if (typeof value === "string") {
    return options.trim !== false ? value.trim() : value;
  }

  if (typeof value === "number" || typeof value === "boolean") {
    const str = String(value);
    return options.trim !== false ? str.trim() : str;
  }

  throw new Error(`Parameter ${key} must be a string, number, or boolean`);
}

/** Supported Twitch actions */
const TWITCH_ACTIONS = new Set(["send" as const]);
type TwitchAction = typeof TWITCH_ACTIONS extends Set<infer U> ? U : never;

/**
 * Twitch message actions adapter.
 */
export const twitchMessageActions: ChannelMessageActionAdapter = {
  /**
   * List available actions for this channel.
   */
  listActions: () => [...TWITCH_ACTIONS],

  /**
   * Check if an action is supported.
   */
  supportsAction: ({ action }) => TWITCH_ACTIONS.has(action as TwitchAction),

  /**
   * Extract tool send parameters from action arguments.
   *
   * Parses and validates the "to" and "message" parameters for sending.
   *
   * @param params - Arguments from the tool call
   * @returns Parsed send parameters or null if invalid
   *
   * @example
   * const result = twitchMessageActions.extractToolSend!({
   *   args: { to: "#mychannel", message: "Hello!" }
   * });
   * // Returns: { to: "#mychannel", message: "Hello!" }
   */
  extractToolSend: ({ args }) => {
    try {
      const to = readStringParam(args, "to", { required: true });
      const message = readStringParam(args, "message", { required: true });

      if (!to || !message) {
        return null;
      }

      return { to, message };
    } catch {
      return null;
    }
  },

  /**
   * Handle an action execution.
   *
   * Processes the "send" action to send messages to Twitch.
   *
   * @param ctx - Action context including action type, parameters, and config
   * @returns Tool result with content or null if action not supported
   *
   * @example
   * const result = await twitchMessageActions.handleAction!({
   *   action: "send",
   *   params: { message: "Hello Twitch!", to: "#mychannel" },
   *   cfg: openclawConfig,
   *   accountId: "default",
   * });
   */
  handleAction: async (ctx: ChannelMessageActionContext) => {
    if (ctx.action !== "send") {
      return {
        content: [{ type: "text" as const, text: "Unsupported action" }],
        details: { ok: false, error: "Unsupported action" },
      };
    }

    const message = readStringParam(ctx.params, "message", { required: true });
    const to = readStringParam(ctx.params, "to", { required: false });
    const accountId = ctx.accountId ?? DEFAULT_ACCOUNT_ID;

    const account = getAccountConfig(ctx.cfg, accountId);
    if (!account) {
      return errorResponse(
        `Account not found: ${accountId}. Available accounts: ${Object.keys(ctx.cfg.channels?.twitch?.accounts ?? {}).join(", ") || "none"}`,
      );
    }

    // Use the channel from account config (or override with `to` parameter)
    const targetChannel = to || account.channel;
    if (!targetChannel) {
      return errorResponse("No channel specified and no default channel in account config");
    }

    if (!twitchOutbound.sendText) {
      return errorResponse("sendText not implemented");
    }

    try {
      const result = await twitchOutbound.sendText({
        cfg: ctx.cfg,
        to: targetChannel,
        text: message ?? "",
        accountId,
      });

      return {
        content: [
          {
            type: "text" as const,
            text: JSON.stringify(result),
          },
        ],
        details: { ok: true },
      };
    } catch (error) {
      const errorMsg = error instanceof Error ? error.message : String(error);
      return errorResponse(errorMsg);
    }
  },
};
