/**
 * OpenClaw 插件入口（瘦装配层）。
 *
 * 把各职责模块实例化并注册到 openclaw 的生命周期 hook：
 *   - before_tool_call / after_tool_call：观测工具调用，记 skill_trigger / function_call
 *   - gateway_start：加载配置、首次对账、起 3 分钟定时器（上报 + 配置对账）
 *   - gateway_stop：停定时器、尽力最后上报一次
 *   - before_install：skill/plugin 安装后重新对账，拉新配置
 *
 * 配置（上报地址/鉴权等）优先从插件 API 初始化配置读取，也兼容 hook 的
 * `event.context.pluginConfig` / `ctx.pluginConfig` 增量注入。
 */
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import os from "node:os";
import type { PluginConfig } from "./types.ts";
import { openclawHome, resolvePaths } from "./paths.ts";
import { ActiveSkills } from "./active-skills.ts";
import { SkillUpdater } from "./updater.ts";
import { ConfigSync } from "./config-sync.ts";
import { Reporter } from "./reporter.ts";
import { repairNestedExpertSkillLayouts } from "./expert-skill-layout.ts";

// 启动长连接中枢
import { GatewayWsClient } from "./ws-client.ts";
let wsClient: GatewayWsClient | undefined;

import { Hooks, isSkillMdReadPath } from "./hooks.ts";

// 供单测复用（保留历史测试）。
export { isSkillMdReadPath };

/** 与 openclaw 插件 SDK 对齐的最小结构类型（结构化 typing，避免硬依赖 SDK 包类型）。 */
type PluginApi = {
  pluginConfig?: PluginConfig;
  on: (
    hookName: string,
    handler: (event: Record<string, unknown>, ctx: Record<string, unknown>) => any
  ) => void;
};

/** 本地扫描对账周期：发现新增 agent/skill（纯本地 I/O），固定 3 分钟。 */
const RECONCILE_INTERVAL_MS = 3 * 60 * 1000;

/** 从 hook 事件/上下文里取本插件的运行期配置。 */
function extractPluginConfig(
  event: Record<string, unknown>,
  ctx: Record<string, unknown>
): PluginConfig | undefined {
  const fromEvent = (event.context as Record<string, unknown> | undefined)?.pluginConfig;
  const fromCtx = ctx?.pluginConfig;
  return (fromEvent ?? fromCtx) as PluginConfig | undefined;
}

export function extractApiPluginConfig(api: Pick<PluginApi, "pluginConfig">): PluginConfig {
  return api.pluginConfig ?? {};
}

const definition = {
  id: "skill-logger-plugin",
  name: "Skill Logger",
  description:
    "追踪 openclaw skill 内功能点（脚本/命令/工具/HTTP）使用与报错，落本地并批量上报",
  register(api: PluginApi) {
    let pkgVersion = "unknown";
    try {
      const dir = path.dirname(fileURLToPath(import.meta.url));
      const pkgPath = path.join(dir, "..", "package.json");
      const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
      if (pkg.version) pkgVersion = pkg.version;
    } catch {
      // 忽略文件读取异常
    }

    const paths = resolvePaths();
    let currentConfig: PluginConfig = extractApiPluginConfig(api);
    currentConfig.pluginVersion = pkgVersion;
    const getConfig = () => currentConfig;
    const mergeConfig = (event: Record<string, unknown>, ctx: Record<string, unknown>) => {
      const incoming = extractPluginConfig(event, ctx);
      if (incoming) currentConfig = { ...currentConfig, ...incoming };
    };

    const activeSkills = new ActiveSkills();
    const updater = new SkillUpdater({ getConfig, cooldownStatePath: paths.cooldownStatePath });
    const configSync = new ConfigSync({ paths, getConfig, updater });
    const reporter = new Reporter({ paths, getConfig });
    const hooks = new Hooks(reporter, configSync, activeSkills, getConfig);

    let expertSkillLayoutRepair: Promise<void> | undefined;
    const repairExpertSkillLayouts = () => {
      if (expertSkillLayoutRepair) return expertSkillLayoutRepair;
      expertSkillLayoutRepair = (async () => {
        const result = await repairNestedExpertSkillLayouts(openclawHome());
        if (result.repaired.length > 0) {
          console.log(`[skill-logger-plugin] 已修复 ${result.repaired.length} 个专家 Skill 嵌套目录`);
        }
        for (const skippedPath of result.skipped) {
          console.warn(`[skill-logger-plugin] 专家 Skill 嵌套目录版本无法安全提升，已保留现场: ${skippedPath}`);
        }
        for (const error of result.errors) {
          console.warn(`[skill-logger-plugin] 修复专家 Skill 目录失败: ${error.path}`, error.message);
        }
      })().finally(() => {
        expertSkillLayoutRepair = undefined;
      });
      return expertSkillLayoutRepair;
    };

    let reconcileTimer: ReturnType<typeof setInterval> | undefined;
    const sessionUpdatedSkills = new Set<string>();

    api.on("message_received", (event, ctx) => {
      mergeConfig(event, ctx);
      hooks.onMessageReceived(event, ctx);
    });

    api.on("before_prompt_build", () => {
      let appendStr = "";
      
      if (sessionUpdatedSkills.size > 0) {
        appendStr = `【系统环境实时通知】：在当前对话期间，以下技能已被更新或重装：[${Array.from(sessionUpdatedSkills).join(", ")}]。如果你之前调用它遇到了报错，请立即抛弃旧的经验，重新阅读它的说明并以最新结果为准！`;
      }
      return { appendSystemContext: appendStr.trim() };
    });

    api.on("before_tool_call", (event, ctx) => {
      mergeConfig(event, ctx);
      hooks.onBeforeToolCall(event, ctx);
    });

    api.on("after_tool_call", (event, ctx) => {
      mergeConfig(event, ctx);
      hooks.onAfterToolCall(event);
    });

    api.on("session_end", (_event, ctx) => {
      hooks.onSessionEnd(ctx);
    });

    api.on("gateway_start", (event, ctx) => {
      mergeConfig(event, ctx);
      
      // =========================================================
      // 启动星型中枢长连接网络
      // =========================================================
      if (!wsClient) {
        const currentConfig = getConfig();
        
        // 优先使用配置文件中的 pluginId，否则回退到 OS hostname
        const uniqueGatewayId = currentConfig.pluginId || process.env.GATEWAY_ID || `gateway-${os.hostname()}`;
        
        // 智能推导 WebSocket 服务地址
        let finalWsUrl = currentConfig.wsServerUrl || process.env.CENTRAL_WS_URL;
        if (!finalWsUrl && currentConfig.platformBaseUrl) {
          try {
            const url = new URL(currentConfig.platformBaseUrl);
            finalWsUrl = `${url.protocol === 'https:' ? 'wss:' : 'ws:'}//${url.host}/gateway/ws`;
          } catch (e) {
            // 容错处理
            finalWsUrl = currentConfig.platformBaseUrl.replace(/^http/, 'ws').replace(/\/api\/?$/, '').replace(/\/$/, '') + '/gateway/ws';
          }
        }
        if (!finalWsUrl) {
          finalWsUrl = "wss://aishuo.co/gateway/ws";
        }
        
        wsClient = new GatewayWsClient({
          serverUrl: finalWsUrl,
          gatewayId: uniqueGatewayId,
          authToken: currentConfig.authToken, // 从 openclaw.json 的 config 节点动态读取鉴权 token
          updater: updater,
          enableFileLog: currentConfig.enableFileLog // 将日志开关透传给客户端模块
        });
      }
      // =========================================================

      // 连接中控前先修复存量异常，避免服务端上线重投与目录迁移并发写入同一路径。
      void repairExpertSkillLayouts()
        .then(() => wsClient?.connect())
        .then(() => configSync.load())
        .then(() => configSync.reconcile())
        .catch((err) => console.warn("[skill-logger-plugin] 启动初始化异常", err));
      reporter.startTimer();
      // 定时器①：本地扫描对账，固定 3 分钟。
      if (!reconcileTimer) {
        reconcileTimer = setInterval(() => void configSync.reconcile(), RECONCILE_INTERVAL_MS);
        if (typeof reconcileTimer.unref === "function") reconcileTimer.unref();
      }
    });

    api.on("gateway_stop", async () => {
      // 停止时销毁连接
      if (wsClient) { 
        wsClient.destroy(); 
        wsClient = undefined; 
      }
      
      if (reconcileTimer) {
        clearInterval(reconcileTimer);
        reconcileTimer = undefined;
      }
      await hooks.flushAllPending();
      await reporter.stopTimer();
    });

    api.on("before_install", (event, ctx) => {
      mergeConfig(event, ctx);
      // 安装后立即对账拉取最新配置，不执行自动更新（由 WSS 指令统一控制更新动作）
      void configSync
        .reconcile()
        .catch((err) => console.warn("[skill-logger-plugin] before_install 处理异常", err));
    });
  },
};

export default definition;
