<script setup>
import { useNamespace, useLocale } from "@yh-ui/hooks";
import { ref, onMounted, onBeforeUnmount, computed, watch, shallowRef, nextTick } from "vue";
import { YhButton } from "../../button";
import { YhIcon } from "../../icon";
import { aiCodeRunnerProps, aiCodeRunnerEmits } from "./ai-code-runner";
import { useComponentTheme } from "@yh-ui/theme";
import { getWebContainerInstance } from "./webcontainer";
defineOptions({
  name: "YhAiCodeRunner"
});
const props = defineProps(aiCodeRunnerProps);
const emit = defineEmits(aiCodeRunnerEmits);
const ns = useNamespace("ai-code-runner");
const { t, lang } = useLocale();
const { themeStyle } = useComponentTheme("ai-code-runner", props.themeOverrides);
const webcontainerInstance = shallowRef(
  null
);
const isRunning = ref(false);
const output = ref([]);
const terminalRef = ref();
const activeProcess = shallowRef(null);
const getLanguageRunConfig = (lang2) => {
  switch (lang2.toLowerCase()) {
    case "typescript":
    case "ts":
      return { filename: "index.ts", command: "npx", args: ["tsx", "index.ts"] };
    case "python":
    case "py":
    case "python3":
      return { filename: "index.py", command: "python3", args: ["index.py"] };
    case "ruby":
    case "rb":
      return { filename: "index.rb", command: "ruby", args: ["index.rb"] };
    case "php":
      return { filename: "index.php", command: "php", args: ["index.php"] };
    case "deno":
      return { filename: "index.ts", command: "deno", args: ["run", "index.ts"] };
    case "javascript":
    case "js":
    default:
      return { filename: "index.js", command: "node", args: ["index.js"] };
  }
};
const terminalHeight = computed(() => {
  if (typeof props.height === "number") {
    return `${props.height}px`;
  }
  return props.height;
});
const addOutput = (data, type = "log") => {
  const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
  const prefix = type === "error" ? "[error]" : ">";
  output.value.push(`[${timestamp}] ${prefix} ${data}`);
  nextTick(() => {
    if (terminalRef.value) {
      terminalRef.value.scrollTop = terminalRef.value.scrollHeight;
    }
  });
};
const initWebContainer = async () => {
  try {
    if (!webcontainerInstance.value) {
      webcontainerInstance.value = await getWebContainerInstance();
      emit("ready", webcontainerInstance.value);
      addOutput(
        lang.value.startsWith("zh") ? "WebContainer \u521D\u59CB\u5316\u5B8C\u6210" : "WebContainer initialization completed",
        "log"
      );
    }
  } catch (error) {
    const errMsg = error instanceof Error ? error.message : "Unknown error";
    addOutput(
      lang.value.startsWith("zh") ? `WebContainer \u521D\u59CB\u5316\u5931\u8D25: ${errMsg}` : `WebContainer initialization failed: ${errMsg}`,
      "error"
    );
    emit("error", errMsg);
  }
};
const runCode = async () => {
  if (!props.code || isRunning.value) return;
  let shellProcess = null;
  try {
    if (!webcontainerInstance.value) {
      await initWebContainer();
    }
    const container = webcontainerInstance.value;
    if (!container) {
      throw new Error(
        lang.value.startsWith("zh") ? "WebContainer \u672A\u5C31\u7EEA" : "WebContainer not ready"
      );
    }
    isRunning.value = true;
    output.value = [];
    addOutput(lang.value.startsWith("zh") ? "\u5F00\u59CB\u6267\u884C\u4EE3\u7801..." : "Starting code execution...", "log");
    emit("run", props.code);
    const runConfig = getLanguageRunConfig(props.language);
    await container.mount({
      [runConfig.filename]: {
        file: {
          contents: props.code
        }
      }
    });
    shellProcess = await container.spawn(runConfig.command, runConfig.args);
    activeProcess.value = shellProcess;
    shellProcess.output.pipeTo(
      new WritableStream({
        write(data) {
          if (activeProcess.value !== shellProcess) return;
          const cleanData = data.replace(/\x1b\[[0-9;]*m/g, "");
          cleanData.split("\n").filter(Boolean).forEach((line) => {
            if (activeProcess.value !== shellProcess) return;
            addOutput(line, "log");
            emit("output", line);
          });
        }
      })
    );
    const exitCode = await shellProcess.exit;
    if (activeProcess.value === shellProcess) {
      if (exitCode === 0) {
        addOutput(lang.value.startsWith("zh") ? "\u6267\u884C\u5B8C\u6210" : "Execution completed", "log");
      } else {
        addOutput(
          lang.value.startsWith("zh") ? `\u6267\u884C\u5931\u8D25\uFF0C\u9000\u51FA\u7801: ${exitCode ?? "unknown"}` : `Execution failed, exit code: ${exitCode ?? "unknown"}`,
          "error"
        );
      }
    }
  } catch (error) {
    if (activeProcess.value === shellProcess || !shellProcess && isRunning.value) {
      const errMsg = error instanceof Error ? error.message : String(error);
      addOutput(
        lang.value.startsWith("zh") ? `\u6267\u884C\u51FA\u9519: ${errMsg}` : `Execution error: ${errMsg}`,
        "error"
      );
      emit("error", errMsg);
    }
  } finally {
    if (activeProcess.value === shellProcess || !shellProcess && isRunning.value) {
      isRunning.value = false;
      if (activeProcess.value === shellProcess) {
        activeProcess.value = null;
      }
    }
  }
};
const stopCode = () => {
  if (activeProcess.value) {
    try {
      activeProcess.value.kill();
    } catch {
    }
    activeProcess.value = null;
  }
  isRunning.value = false;
  addOutput(lang.value.startsWith("zh") ? "\u6267\u884C\u5DF2\u505C\u6B62" : "Execution stopped", "log");
  emit("stop");
};
const clearOutput = () => {
  output.value = [];
};
const reset = async () => {
  stopCode();
  output.value = [];
  if (webcontainerInstance.value) {
    webcontainerInstance.value = null;
    addOutput(lang.value.startsWith("zh") ? "WebContainer \u5DF2\u91CD\u7F6E" : "WebContainer reset", "log");
  }
};
onMounted(() => {
  if (props.autorun && props.code) {
    runCode();
  }
});
onBeforeUnmount(() => {
  if (activeProcess.value) {
    try {
      activeProcess.value.kill();
    } catch {
    }
    activeProcess.value = null;
  }
  if (webcontainerInstance.value) {
    webcontainerInstance.value = null;
  }
});
watch(
  () => props.code,
  () => {
    if (props.autorun) {
      if (isRunning.value) stopCode();
      runCode();
    }
  }
);
defineExpose({
  run: runCode,
  stop: stopCode,
  reset,
  clear: clearOutput
});
</script>

<template>
  <div :class="ns.b()" :style="themeStyle">
    <div :class="ns.e('toolbar')">
      <YhButton v-if="!isRunning" type="primary" size="small" :disabled="!code" @click="runCode">
        <template #icon>
          <YhIcon name="video-play" />
        </template>
        {{ t('ai.codeRunner.run') }}
      </YhButton>
      <YhButton v-else type="danger" size="small" @click="stopCode">
        <template #icon>
          <YhIcon name="video-pause" />
        </template>
        {{ t("ai.codeRunner.stop") }}
      </YhButton>
      <YhButton size="small" @click="clearOutput">
        <template #icon>
          <YhIcon name="delete" />
        </template>
        {{ t("ai.codeRunner.clear") }}
      </YhButton>
      <YhButton size="small" @click="reset">
        <template #icon>
          <YhIcon name="refresh" />
        </template>
        {{ t("ai.codeRunner.reset") }}
      </YhButton>
    </div>
    <div ref="terminalRef" :class="ns.e('terminal')" :style="{
  height: terminalHeight
}">
      <div v-if="output.length === 0" :class="ns.e('placeholder')">
        {{ t("ai.codeRunner.placeholder") }}
      </div>
      <div
        v-for="(line, index) in output"
        :key="index"
        :class="[ns.e('line'), line.includes('[error]') ? ns.m('error') : '']"
      >
        {{ line }}
      </div>
    </div>
  </div>
</template>

<style>
@charset "UTF-8";
/**
 * YH-UI Sass Variables
 * 原具体的 CSS 变量输出规则 (:root, html.dark 等) 已移至 root.scss，以防止各组件样式编译时产生重复的全局变量 CSS 冗余。
 */
.yh-ai-code-runner {
  /* 组件级 CSS 变量，便于 themeOverrides 覆盖，保持与现有 AI 组件一致 */
  --yh-ai-code-runner-bg: #1e1e1e;
  --yh-ai-code-runner-toolbar-bg: #2d2d2d;
  --yh-ai-code-runner-text: #d4d4d4;
  --yh-ai-code-runner-placeholder: #6a6a6a;
  --yh-ai-code-runner-error: var(--yh-color-danger);
  display: flex;
  flex-direction: column;
  border: 1px solid var(--yh-border-color);
  border-radius: var(--yh-radius-md);
  overflow: hidden;
  background: var(--yh-ai-code-runner-bg);
}
.yh-ai-code-runner__toolbar {
  display: flex;
  gap: var(--yh-spacing-sm);
  padding: var(--yh-spacing-sm) var(--yh-spacing-md);
  background: var(--yh-ai-code-runner-toolbar-bg);
  border-bottom: 1px solid var(--yh-border-color);
}

.yh-ai-code-runner__terminal {
  flex: 1;
  min-height: 0;
  padding: var(--yh-spacing-md);
  overflow-y: auto;
  font-family: var(--yh-font-family-monospace, "Fira Code", "Consolas", monospace);
  font-size: var(--yh-font-size-sm);
  line-height: var(--yh-line-height-relaxed);
  color: var(--yh-ai-code-runner-text);
}
.yh-ai-code-runner__terminal::-webkit-scrollbar {
  width: 6px;
}
.yh-ai-code-runner__terminal::-webkit-scrollbar-thumb {
  background-color: var(--yh-scrollbar-thumb-color);
  border-radius: 3px;
}

.yh-ai-code-runner__placeholder {
  color: var(--yh-ai-code-runner-placeholder);
  font-style: italic;
}

.yh-ai-code-runner__line {
  white-space: pre-wrap;
  word-break: break-all;
  margin-bottom: 2px;
}
.yh-ai-code-runner__line.yh-ai-code-runner--error {
  color: var(--yh-ai-code-runner-error);
}
</style>
