﻿import { Context, h, Session, Universal } from "koishi";
import { Config, usage } from "./config";
import { logInfo, logError, logInfoformat, replacePlaceholders } from "./utils";
import { determineImagePath, getRandomEmojiHubCommand, listAllCommands, } from "./core";
import { markdown, command_list_markdown, sendmarkdownMessage } from "./markdown";
import { loadCanvasImageSource } from "./canvas-source";
import { registerLocalImageProxyRoute } from "./media-proxy";

import { } from '@koishijs/assets';
import { } from '@koishijs/plugin-server';
import { } from "koishi-plugin-cron";
import { } from "koishi-plugin-canvas";

export const inject = {
  required: ['server'],
  optional: ['canvas', 'assets']
};

export const name = 'emojihub-bili';
export const reusable = true;

export { Config, usage };

export function apply(ctx: Context, config: Config) {
  const emojihub_bili_codecommand = config.emojihub_bili_command;
  registerLocalImageProxyRoute(ctx);
  async function getLocalImageSize(canvasSource: string) {
    if (!ctx.canvas) throw new Error('canvas service not available');
    logInfo(config, `获取本地图片尺寸，canvasSource: ${canvasSource}`);
    const canvasimage = await loadCanvasImageSource(ctx, canvasSource);
    // @ts-ignore
    const width = canvasimage.naturalWidth || canvasimage.width;
    // @ts-ignore
    const height = canvasimage.naturalHeight || canvasimage.height;
    return { width, height };
  }

  async function resolveAssetsUrlForMarkdown(imageUrl: string, imagePath?: string) {
    if (!ctx.assets) throw new Error('assets service not available');
    const localTarget = imageUrl || imagePath;
    const transformed = await ctx.assets.transform(String(h.image(localTarget)));
    const match = transformed.match(/<img\s+src="([^"]+)"/i);
    if (!match?.[1]) throw new Error(`assets.transform did not return an image url: ${transformed}`);
    return h.unescape(match[1]);
  }

  ctx.i18n.define("zh-CN",
    {
      commands: {
        [emojihub_bili_codecommand]: {
          description: `表情包功能`,
          messages: {
            "notfound_txt": "ERROR！找不到文件或文件为空！指令：{0}",
            "List_of_emojis": "可用的表情包指令：{0}",
            "notallowednum": `{0}次超出单次返回最大值\n请使用指令：{1} -n {2}`,
          }
        },
        [config.emojihub_onemore]: {
          description: `触发上次的表情包`,
          messages: {
            "nocommand": `没有找到上一个命令，请先执行一个命令！\n➣例如： ${config.emojihub_randompic}`,
          }
        },
        [config.emojihub_randompic]: {
          description: `从全部表情包里随机抽`,
          messages: {
            "noemoji": "没有任何表情包配置，请检查插件配置项！",
          }
        }
      }
    }
  );

  const lastCommandByChannel = {};

  function updateLastCommand(differentiationID: string, command: string) {
    lastCommandByChannel[differentiationID] = command;
    logInfo(config, '记录到command为： ' + command + ' 区别ID： ' + differentiationID);
  }

  async function sendMultipleEmojis(session: Session, command: string, num: number) {
    const maxAllowed = config.maxexecutetime || 10; // 使用配置中的最大数量，默认为10
    if (num > maxAllowed) {
      await session.send(h.text(session.text(`commands.${emojihub_bili_codecommand}.messages.notallowednum`, [num, command, maxAllowed])));
      return; // 不继续执行
    }
    const numToSend = Math.min(num || 1, maxAllowed); // 确定要发送的数量，不超过最大值
    for (let i = 0; i < numToSend; i++) {
      // 如果是“再来一张”指令，则需要特殊处理
      if (command === config.emojihub_onemore) {
        const identifier = config.repeatCommandDifferentiation === 'userId' ? session.userId : session.channelId;
        const lastCommand = lastCommandByChannel[identifier];
        if (lastCommand) {
          await session.execute(lastCommand);
        } else {
          await session.send(session.text(".nocommand"));
          return; // 如果没有上一个命令，则直接返回，不再继续循环
        }
      } else {
        // 对于其他指令，直接执行
        await session.execute(command);
      }
    }
  }

  ctx.command(config.emojihub_bili_command)
    .action(async ({ session }) => {
      const txtCommandList = listAllCommands(config);
      logInfo(config, `指令列表txtCommandList：  ` + txtCommandList);

      if (session.platform === "qq" || session.platform === "qqguild") {
        let markdownMessage = command_list_markdown(ctx, session, config, emojihub_bili_codecommand, 1);
        await sendmarkdownMessage(ctx, session, markdownMessage, config);
      } else {
        const commandText = txtCommandList.join('\n');
        await session.send(h.text(session.text(`commands.${emojihub_bili_codecommand}.messages.List_of_emojis`, [`\n${commandText}`])));
      }
    });

  ctx.on('ready', () => {
    config.MoreEmojiHubList.forEach(({ command, source_url }) => {
      ctx.command(`${config.emojihub_bili_command}/${command} [local_picture_name...]`)
        .example(`${command} 关键词1 关键词2 关键词3`)
        .option('numpics', `-n <numpics:number> 指定返回数量`)
        .action(async ({ session, options }, ...local_picture_name) => {
          if (options?.numpics) {
            await sendMultipleEmojis(session, `${command} ${local_picture_name.join(' ')}`.trim(), options.numpics);
            return;
          }
          const imageResult = await determineImagePath(source_url, config, session.channelId, command, ctx, local_picture_name);

          if (!imageResult.imageUrl) {
            await session.send(h.text(session.text(`commands.${emojihub_bili_codecommand}.messages.notfound_txt`, [command])));
            return;
          }

          // 根据 config.repeatCommandDifferentiation 的值选择合适的 ID
          const identifier = config.repeatCommandDifferentiation === 'userId' ? session.userId : session.channelId;
          updateLastCommand(identifier, command);

          try {
            let message;
            if ((session.platform === "qq" || session.platform === "qqguild") && (config.markdown_button_mode === "raw")) {
              if (imageResult.isLocal) {
                const imageSize = imageResult.imageUrl ? await getLocalImageSize(imageResult.imageUrl) : undefined;
                const assetsUrl = await resolveAssetsUrlForMarkdown(imageResult.imageUrl, imageResult.imagePath);
                logInfo(config, `[assets] converted local image to url: ${assetsUrl}`);
                message = await markdown(ctx, session, command, assetsUrl, config, imageResult.imageUrl, imageSize);
                await sendmarkdownMessage(ctx, session, message, config);
              } else {
                message = await markdown(ctx, session, command, imageResult.imageUrl, config);
                await sendmarkdownMessage(ctx, session, message, config);
              }
            } else {
              if (imageResult.isLocal) {
                const format = config.localPictureToName;
                logInfo(config, imageResult.imageUrl)
                // 格式化文件大小
                const fileSizeKB = (imageResult.imageSize / 1024).toFixed(2);
                const fileSizeMB = (imageResult.imageSize / (1024 * 1024)).toFixed(2);
                const formattedSize = imageResult.imageSize < 1024 * 1024 ? `${fileSizeKB} KB` : `${fileSizeMB} MB`;
                // 格式化时间
                const formattedTime = imageResult.imageTime.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }).replace(/\//g, '-');

                const imageBuffer = Buffer.from((await ctx.http.file(imageResult.imageUrl)).data);
                const base64Data = imageBuffer.toString('base64');

                const assetsUrl = `data:image/png;base64,${base64Data}`;
                const context = {
                  IMAGE: h.image(assetsUrl),
                  NAME: imageResult.imageName,
                  TIME: formattedTime,
                  SIZE: formattedSize,
                  PATH: imageResult.imagePath,
                };
                const messageContent = replacePlaceholders(format, context);
                logInfo(config, "变量替换本地文件名称，messageContent：", messageContent)
                try {
                  message = await session.send(h.unescape(`${messageContent}`.replace(/\\n/g, '\n')));
                } catch (error) {
                  ctx.logger.error("发送本地图片失败：", error)
                }

              } else { // 网络图片
                try {
                  message = await session.send(h.image(imageResult.imageUrl));
                } catch (error) {
                  ctx.logger.error("发送网络图片失败：", error)
                }
              }
            }

            if (config.deleteMsg) {
              ctx.setTimeout(async () => {
                try {
                  await session.bot.deleteMessage(session.channelId, message);
                } catch (error) {
                  logError(`撤回消息失败: ${error}`);
                  logError(error);
                }
              }, config.deleteMsgtime * 1000);
            }
          } catch (error) {
            logError(`Error sending image:  ${error}`);
            logError(error)
          }
        });
    })
  });

  ctx.command(`${config.emojihub_bili_command}/${config.emojihub_onemore}`)
    .action(async ({ session, options }) => {
      const identifier = config.repeatCommandDifferentiation === 'userId' ? session.userId : session.channelId;
      const lastCommand = lastCommandByChannel[identifier];

      logInfo(config, '尝试在区分ID ' + identifier + ' 中执行最后一个命令： ' + lastCommand);
      if (lastCommand) {
        await session.execute(`${lastCommand}`);
      } else {
        await session.send(session.text(".nocommand"));
      }
    });

  ctx.command(`${config.emojihub_bili_command}/${config.emojihub_randompic}`)
    .action(async ({ session, options }) => {

      const randomEmojiHubCommand = getRandomEmojiHubCommand(config);
      if (randomEmojiHubCommand) {
        await session.execute(randomEmojiHubCommand);
        logInfoformat(config, session.channelId, randomEmojiHubCommand, config.emojihub_randompic);
        return;
      } else {
        await session.send(session.text(".noemoji"));
      }
    });

  if (config.autoEmoji === "定量消息发送" && (config.groupListmapping.length || config.allgroupautoEmoji)) {
    const groups = {};
    // 初始化特定群组的配置
    config.groupListmapping.forEach(({ groupList, defaultemojicommand, count, enable }) => {
      if (enable === true) {
        // 如果enable为true，则将该群组标记为黑名单
        groups[groupList] = { blacklisted: true };
      } else {
        groups[groupList] = { emojicommand: defaultemojicommand, threshold: count };
      }
    });

    ctx.middleware(async (session, next) => {
      const channelId = session.channelId;

      // 确定当前群组是否在特定配置中并且是否被黑名单
      let groupConfig = groups[channelId];

      // 如果当前群组标记为黑名单，则直接跳过处理
      if (groupConfig && groupConfig.blacklisted) {
        return next();
      }

      // 如果当前群组没有特定配置，并且开启了全部群组自动表情包
      if (!groupConfig && config.allgroupautoEmoji) {
        // 初始化为全部群组的配置
        groupConfig = {
          count: 0,
          emojicommand: config.allgroupemojicommand,
          threshold: config.count
        };
        groups[channelId] = groupConfig; // 记录配置以供后续使用
      }

      // 如果存在配置，处理表情包逻辑
      if (groupConfig) {
        groupConfig.count = (groupConfig.count || 0) + 1; // 增加消息计数
        logInfo(config, `${channelId} ：${groupConfig.count} ：${session.content}`)
        // 达到触发条件
        if (groupConfig.count >= groupConfig.threshold) {
          const randomNumber = Math.random();
          // 触发概率判断
          if (randomNumber <= config.triggerprobability) {
            logInfo(config, `定量消息发送：概率判断：${randomNumber} <= ${config.triggerprobability} 触发表情包`) // 打印触发日志
            let emojicommands = groupConfig.emojicommand.split(/\n|,|，/).map(cmd => cmd.trim());
            const randomCommand = emojicommands[Math.floor(Math.random() * emojicommands.length)];
            logInfo(config, `随机选择的指令: ${randomCommand}`);
            const emojiConfig = config.MoreEmojiHubList.find(({ command }) => command === randomCommand);
            if (emojiConfig) {
              const imageResult = await determineImagePath(emojiConfig.source_url, config, channelId, emojiConfig.command, ctx);
              if (imageResult.imageUrl) {
                try {
                  groupConfig.count = 0; // 重置消息计数
                  let message;
                  if (imageResult.isLocal) { //本地图片
                    message = h.image(imageResult.imageUrl);
                  } else {
                    message = h.image(imageResult.imageUrl);
                  }
                  let sentMessage = await session.send(message);
                  // 如果需要撤回消息
                  if (config.deleteMsg) {
                    ctx.setTimeout(async () => {
                      try {
                        await session.bot.deleteMessage(session.channelId, sentMessage[0]);
                      } catch (error) {
                        logError(`撤回消息失败: ${error}`);
                      }
                    }, config.deleteMsgtime * 1000);
                  }
                } catch (error) {
                  logError(`发送图片错误: ${error}`);
                }
              } else {
                groupConfig.count = 0; // 图片不存在，重置计数
              }
            }
          } else {
            groupConfig.count = 0; // 没有触发表情包，重置计数
            const comparisonSymbol = randomNumber <= config.triggerprobability ? "<=" : ">"; // 根据比较结果设置比较符号
            logInfo(config, `定量消息发送：概率判断：${randomNumber} ${comparisonSymbol} ${config.triggerprobability}\n此次不发送表情包，并且重置计数。`)
          }
        }
      }
      return next();
    }, config.middleware);
  }


  ctx.on('ready', () => {
    if (config.autoEmoji === "定时发送" && config.groupListmapping.length && ctx.cron) {
      // const bot = ctx.bots[config.bot];
      const bot = Object.values(ctx.bots).find(b => b.selfId === config.botId || b.user?.id === config.botId);
      if (!bot || bot.status !== Universal.Status.ONLINE) {
        ctx.logger.error(`[定时发送] 机器人离线或未找到: ${config.botId}`);
        return;
      } else {
        ctx.logger.info(`定时成功：将由 ${config.botId} 执行`);
      }
      if (bot == null) return;

      const groups = {};
      // 初始化特定群组的配置
      config.groupListmapping.forEach(({ groupList, defaultemojicommand, cronTime, enable }) => {
        if (enable === true) {
          // 如果enable为true，则将该群组标记为黑名单
          groups[groupList] = { blacklisted: true };
        } else {
          groups[groupList] = { emojicommand: defaultemojicommand, cronTime };
        }
      });

      // 定时触发表情包
      for (const channelId in groups) {
        const groupConfig = groups[channelId];

        // 如果当前群组标记为黑名单，则跳过处理
        if (groupConfig && groupConfig.blacklisted) {
          continue;
        }

        // 如果当前群组没有特定配置，则跳过
        if (!groupConfig) {
          continue;
        }

        // 如果存在配置，设置定时任务
        if (groupConfig) {
          ctx.inject(['cron'], (ctx) => {
            ctx.cron(groupConfig.cronTime, async () => {
              const randomNumber = Math.random();
              // 触发概率判断
              if (randomNumber <= config.triggerprobability) {
                logInfo(config, `尝试向 ${channelId} 定时发送表情包中...`)
                let emojicommands = groupConfig.emojicommand.split(/\n|,|，/).map(cmd => cmd.trim());
                const randomCommand = emojicommands[Math.floor(Math.random() * emojicommands.length)];
                const emojiConfig = config.MoreEmojiHubList.find(({ command }) => command === randomCommand);
                if (emojiConfig) {
                  const imageResult = await determineImagePath(emojiConfig.source_url, config, channelId, emojiConfig.command, ctx);
                  if (imageResult.imageUrl) {
                    try {
                      let message;
                      if (imageResult.isLocal) { //本地图片
                        message = h.image(imageResult.imageUrl);
                      } else {
                        message = h.image(imageResult.imageUrl);
                      }

                      // 判断是群聊还是私聊
                      if (!channelId.includes("private")) {
                        await bot.sendMessage(channelId, message);
                      } else {
                        const userId = channelId.replace("private:", "");
                        await bot.sendPrivateMessage(userId, message);
                      }

                      // 如果需要撤回消息
                      if (config.deleteMsg) {
                        ctx.setTimeout(async () => {
                          try {
                            await bot.deleteMessage(channelId, message);
                          } catch (error) {
                            logError(`撤回消息失败: ${error}`);
                          }
                        }, config.deleteMsgtime * 1000);
                      }
                    } catch (error) {
                      logError(`发送图片错误: ${error}`);
                    }
                  }
                }
              } else {
                const comparisonSymbol = randomNumber <= config.triggerprobability ? "<=" : ">"; // 根据比较结果设置比较符号
                logInfo(config, `定时发送：概率判断结果：${randomNumber} ${comparisonSymbol} ${config.triggerprobability}\n此次不发送表情包。`)
              }
            });
          })
        }
      }
    } else if (config.autoEmoji === "定时发送" && config.groupListmapping.length && !ctx.cron) {
      ctx.logger.error("cron 服务加载失败！")
    }
  })
}
