<script setup lang="ts">
import { ref, watch, nextTick, computed } from "vue";
import { AttachmentsService } from "@/components/service/management/AttachmentsService";
import { KubeflowService } from "@/components/service/management/KubeflowService";
import {
  toKubeflowForm,
  type KubeflowUploadDto,
} from "@/components/models/management/Kubeflow";
import { commonStore } from "@/stores/commonStore";
import { storeToRefs } from "pinia";
import { useAutoflowStore } from "@/stores/autoflowStore";
import { storage } from "@/utils/storage";

const store = commonStore();
const { projectId } = storeToRefs(useAutoflowStore());
const logPreRef = ref<HTMLElement | null>(null);
function scrollLogToBottom() {
  nextTick(() => {
    if (logPreRef.value) logPreRef.value.scrollTop = logPreRef.value.scrollHeight;
  });
}

const props = withDefaults(
  defineProps<{
    modelValue: boolean;
    item: { deviceKey?: number; id?: number; title?: string; fileName?: string; filePath?: string } | null;
  }>(),
  { modelValue: false, item: null }
);

const emit = defineEmits<{
  (e: "update:modelValue", v: boolean): void;
  (e: "compiled"): void;
}>();

const open = ref(props.modelValue);
const logText = ref("");
const loading = ref(false);
/** 컴파일 성공 시 반환된 YAML MinIO 경로 (다운로드 버튼 표시용) */
const lastYamlPath = ref<string | null>(null);

watch(
  () => props.modelValue,
  async (v) => {
    open.value = v;
    if (v) {
      logText.value = "[대기] 컴파일 버튼을 누르면 실행됩니다.\n";
      loading.value = false;
      lastYamlPath.value = null;

      const id = props.item?.deviceKey ?? props.item?.id;
      if (id != null) {
        try {
          const res = await AttachmentsService.getCompiledInfo(Number(id));
          const data = res?.data ?? res;
          const yamlPath = data?.yamlStoragePath;
          if (yamlPath) {
            lastYamlPath.value = yamlPath;
            appendLog("이미 컴파일된 YAML이 있어 바로 다운로드할 수 있습니다.");
            appendLog(`저장 경로: ${yamlPath}`);
          }
        } catch (err: any) {
          // 204(no content) 등은 단순히 무시, 오류 메시지는 로그에만 남김
          console.debug("[ScriptCompile] 기존 컴파일 정보 조회 실패 또는 없음:", err);
        }
      }
    }
  },
  { immediate: true }
);
watch(open, (v) => emit("update:modelValue", v));

function appendLog(line: string) {
  const ts = new Date().toLocaleTimeString("ko-KR", { hour12: false });
  logText.value += `[${ts}] ${line}\n`;
  scrollLogToBottom();
}

async function runCompile() {
  const id = props.item?.deviceKey ?? props.item?.id;
  if (id == null) {
    appendLog("[오류] 스크립트 ID가 없습니다.");
    return;
  }
  loading.value = true;
  appendLog("컴파일 시작... (API 호출 중)");
  try {
    const res = await AttachmentsService.compile(Number(id));
    const data = res?.data ?? res;
    const yamlPath = data?.yamlStoragePath;
    if (yamlPath) {
      lastYamlPath.value = yamlPath;
      appendLog(`성공: KFP 학습 실행용 YAML이 생성되었습니다.`);
      appendLog(`저장 경로: ${yamlPath}`);
      emit("compiled");
    } else {
      appendLog("완료: (경로 정보 없음)");
    }
  } catch (err: any) {
    const dataMsg = err?.response?.data?.error ?? err?.response?.data?.message;
    const msg = dataMsg ?? err?.message ?? "컴파일 요청 실패";
    appendLog("실패:");
    appendLog(msg);
  } finally {
    loading.value = false;
  }
}

function closeDialog() {
  open.value = false;
}

function filenameFromPath(path: string): string {
  return path.includes("/") ? path.substring(path.lastIndexOf("/") + 1) : path;
}

async function downloadYaml() {
  const path = lastYamlPath.value;
  if (!path) return;
  try {
    const res = await AttachmentsService.downloadFile(path);
    const ct = String(res.headers["content-type"] || "").toLowerCase();
    if (ct.includes("application/json")) {
      const text = await (res.data as Blob).text();
      try {
        const json = JSON.parse(text);
        store.setSnackbarMsg({ color: "warning", text: json.message || text, result: 400 });
      } catch {
        store.setSnackbarMsg({ color: "warning", text: text, result: 400 });
      }
      return;
    }
    const cd = res.headers["content-disposition"] || "";
    const mUtf8 = cd.match(/filename\*\s*=\s*UTF-8''([^;]+)/i);
    const mStd = cd.match(/filename\s*=\s*(?:"([^"]+)"|([^;]+))/i);
    const filename =
      (mUtf8 && decodeURIComponent(mUtf8[1].trim())) ||
      (mStd && (mStd[1] || mStd[2])?.trim()) ||
      filenameFromPath(path) ||
      "pipeline.yaml";
    const blob = new Blob([res.data]);
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.setAttribute("download", filename);
    document.body.appendChild(a);
    a.click();
    a.remove();
    URL.revokeObjectURL(url);
    store.setSnackbarMsg({ color: "success", text: "YAML이 다운로드되었습니다.", result: 200 });
  } catch (e) {
    console.error("[ScriptCompile] YAML 다운로드 실패:", e);
    store.setSnackbarMsg({ color: "warning", text: "YAML 다운로드에 실패했습니다.", result: 500 });
  }
}

async function downloadBundle() {
  const path = lastYamlPath.value;
  const id = props.item?.deviceKey ?? props.item?.id;
  if (!path || id == null) return;
  try {
    const res = await AttachmentsService.downloadCompiledBundle(Number(id), path);
    const ct = String(res.headers["content-type"] || "").toLowerCase();
    if (ct.includes("application/json")) {
      const text = await (res.data as Blob).text();
      try {
        const json = JSON.parse(text);
        store.setSnackbarMsg({ color: "warning", text: json.message || text, result: 400 });
      } catch {
        store.setSnackbarMsg({ color: "warning", text: text, result: 400 });
      }
      return;
    }
    const cd = res.headers["content-disposition"] || "";
    const mUtf8 = cd.match(/filename\*\s*=\s*UTF-8''([^;]+)/i);
    const mStd = cd.match(/filename\s*=\s*(?:"([^"]+)"|([^;]+))/i);
    const base = filenameFromPath(path).replace(/\.yaml$/i, "");
    const filename =
      (mUtf8 && decodeURIComponent(mUtf8[1].trim())) ||
      (mStd && (mStd[1] || mStd[2])?.trim()) ||
      `${base}_bundle.zip`;
    const blob = new Blob([res.data]);
    const url = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = url;
    a.setAttribute("download", filename);
    document.body.appendChild(a);
    a.click();
    a.remove();
    URL.revokeObjectURL(url);
    store.setSnackbarMsg({ color: "success", text: "YAML + 스크립트(ZIP)가 다운로드되었습니다.", result: 200 });
  } catch (e) {
    console.error("[ScriptCompile] 번들 다운로드 실패:", e);
    store.setSnackbarMsg({ color: "warning", text: "번들 다운로드에 실패했습니다.", result: 500 });
  }
}

// ----- Workflows 등록 (Create Workflow와 동일한 name/description 입력 후 등록) -----
const KFP_NAME_REGEX = /^[a-z0-9]([-a-z0-9\.]*[a-z0-9])?$/;
const sanitizeKfpName = (s: string) => {
  let x = (s ?? "").toLowerCase();
  x = x.replace(/[\s_]+/g, "-");
  x = x.replace(/[^a-z0-9.-]/g, "");
  x = x.replace(/-+/g, "-");
  x = x.replace(/^[^a-z0-9]+/, "");
  x = x.replace(/[^a-z0-9]+$/, "");
  return x;
};

const showRegisterWorkflow = ref(false);
const registerForm = ref({ name: "", description: "" });
const registerLoading = ref(false);
const registerError = ref("");

const registerNameHint = ref(
  "허용: 소문자, 숫자, '-', '.' (시작/끝은 영숫자)",
);
const registerNameInvalid = computed(
  () => !!registerForm.value.name && !KFP_NAME_REGEX.test(registerForm.value.name),
);
const registerNameErrorMsg = computed(() =>
  registerNameInvalid.value
    ? "형식이 올바르지 않습니다. (소문자/숫자, '-', '.')"
    : "",
);

function openRegisterWorkflowDialog() {
  registerError.value = "";
  const base = (props.item?.title ?? props.item?.fileName ?? "pipeline") as string;
  registerForm.value.name = sanitizeKfpName(base) || "training-pipeline";
  registerForm.value.description = "";
  showRegisterWorkflow.value = true;
}

function onRegisterNameInput(v: string) {
  registerForm.value.name = sanitizeKfpName(v || "");
}

async function submitRegisterWorkflow() {
  registerError.value = "";
  const name = sanitizeKfpName(registerForm.value.name).trim();
  const description = (registerForm.value.description ?? "").trim();

  if (!name || !KFP_NAME_REGEX.test(name)) {
    registerError.value = "Workflow Name 형식이 올바르지 않습니다.";
    return;
  }

  const authObj =
    (typeof storage?.getAuth === "function" ? storage.getAuth() : null) ??
    JSON.parse(localStorage.getItem("autoflow-auth") || "{}");
  const ui = authObj?.userInfo ?? authObj?.userinfo ?? authObj ?? {};
  const userId = Number(ui.id);
  if (!userId) {
    registerError.value = "로그인 사용자 정보를 찾을 수 없습니다.";
    return;
  }
  if (!projectId.value) {
    registerError.value = "프로젝트가 선택되지 않았습니다.";
    return;
  }

  const path = lastYamlPath.value;
  if (!path) {
    registerError.value = "컴파일된 YAML이 없습니다. 먼저 컴파일을 실행하세요.";
    return;
  }

  try {
    registerLoading.value = true;
    const res = await AttachmentsService.downloadFile(path);
    const ct = String(res.headers["content-type"] || "").toLowerCase();
    if (ct.includes("application/json")) {
      const text = await (res.data as Blob).text();
      registerError.value = JSON.parse(text)?.message || text || "YAML 다운로드 실패";
      return;
    }
    const blob = res.data instanceof Blob ? res.data : new Blob([res.data]);
    const fileName = filenameFromPath(path) || "pipeline.yaml";
    const file = new File([blob], fileName, { type: "application/x-yaml" });

    const dto: KubeflowUploadDto = {
      name,
      display_name: name,
      description,
      namespace: "default",
      regUserId: userId,
      projectId: projectId.value!,
      uploadfile: file,
    };
    const fd = toKubeflowForm(dto);
    await KubeflowService.upload(fd);
    store.setSnackbarMsg({ color: "success", text: "Workflow에 등록되었습니다.", result: 200 });
    showRegisterWorkflow.value = false;
    emit("compiled");
  } catch (e: any) {
    console.error("[ScriptCompile] Workflow 등록 실패:", e);
    const msg =
      e?.response?.data?.message ?? e?.response?.data?.error ?? e?.message ?? "Workflow 등록에 실패했습니다.";
    registerError.value = msg;
    store.setSnackbarMsg({ color: "warning", text: msg, result: e?.response?.status ?? 500 });
  } finally {
    registerLoading.value = false;
  }
}
</script>

<template>
  <v-dialog :model-value="open" max-width="720" persistent @update:model-value="(v) => (open = v)">
    <v-card>
      <v-card-title class="d-flex align-center">
        <span class="font-weight-bold">스크립트 컴파일</span>
        <v-spacer />
        <v-btn icon variant="text" size="small" @click="closeDialog">
          <v-icon>mdi-close</v-icon>
        </v-btn>
      </v-card-title>
      <v-divider />
      <v-card-text>
        <template v-if="item">
          <div class="text-body-2 mb-3">
            <div><strong>제목:</strong> {{ item.title ?? "—" }}</div>
            <div><strong>파일명:</strong> {{ item.fileName ?? "—" }}</div>
            <div v-if="item.filePath" class="text-medium-emphasis" style="word-break: break-all;">
              <strong>경로:</strong> {{ item.filePath }}
            </div>
          </div>
          <v-divider class="my-3" />
          <div class="text-subtitle-2 font-weight-medium mb-1">로그</div>
          <pre
            ref="logPreRef"
            class="compile-log pa-3 overflow-auto rounded"
            style="max-height: 360px; min-height: 120px; white-space: pre-wrap; word-break: break-all; font-size: 0.85rem;"
          >{{ logText || " " }}</pre>
        </template>
      </v-card-text>
      <v-divider />
      <v-card-actions class="justify-end pa-4 flex-wrap">
        <v-btn v-if="lastYamlPath" variant="outlined" size="small" @click="downloadYaml">
          YAML 다운로드
        </v-btn>
        <v-btn v-if="lastYamlPath" variant="outlined" size="small" @click="downloadBundle">
          YAML + 스크립트 다운로드 (ZIP)
        </v-btn>
        <v-btn
          v-if="lastYamlPath"
          color="primary"
          variant="outlined"
          size="small"
          @click="openRegisterWorkflowDialog"
        >
          Workflows 등록
        </v-btn>
        <v-spacer />
        <v-btn variant="text" @click="closeDialog">닫기</v-btn>
        <v-btn
          color="primary"
          variant="elevated"
          :loading="loading"
          :disabled="!item || (item?.deviceKey == null && item?.id == null)"
          @click="runCompile"
        >
          컴파일 실행
        </v-btn>
      </v-card-actions>
    </v-card>

    <!-- Workflows 등록: 이름·설명 입력 다이얼로그 (Create Workflow와 동일) -->
    <v-dialog
      v-model="showRegisterWorkflow"
      max-width="520"
      persistent
      scrollable
    >
      <v-card>
        <v-card-title class="text-white font-weight-bold text-h6" style="background-color: #1976d2">
          Workflow 등록
        </v-card-title>
        <v-divider />
        <v-card-text class="pa-5">
          <div class="text-subtitle-2 font-weight-medium mb-2">Workflow Name</div>
          <v-text-field
            :model-value="registerForm.name"
            variant="outlined"
            density="comfortable"
            hide-details="auto"
            persistent-hint
            :hint="registerNameHint"
            :error="registerNameInvalid"
            :error-messages="registerNameErrorMsg"
            :disabled="registerLoading"
            @update:model-value="onRegisterNameInput"
          />
          <div class="text-subtitle-2 font-weight-medium mb-2 mt-4">Workflow Description</div>
          <v-textarea
            v-model="registerForm.description"
            variant="outlined"
            density="comfortable"
            rows="3"
            hide-details="auto"
            :disabled="registerLoading"
          />
          <div v-if="registerError" class="mt-3 text-error text-body-2">{{ registerError }}</div>
        </v-card-text>
        <v-divider />
        <v-card-actions class="justify-end pa-4">
          <v-btn variant="text" :disabled="registerLoading" @click="showRegisterWorkflow = false">
            취소
          </v-btn>
          <v-btn
            color="primary"
            variant="elevated"
            :loading="registerLoading"
            :disabled="registerNameInvalid"
            @click="submitRegisterWorkflow"
          >
            등록
          </v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </v-dialog>
</template>

<style scoped>
.compile-log {
  background-color: rgba(0, 0, 0, 0.05);
  border: 1px solid rgba(0, 0, 0, 0.08);
}
</style>
