<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import { AttachmentsService } from "@/components/service/management/AttachmentsService";
import { useAutoflowStore } from "@/stores/autoflowStore";
import { storeToRefs } from "pinia";

const props = withDefaults(
  defineProps<{ currentRefId?: number | null }>(),
  { currentRefId: null }
);

const emit = defineEmits<{
  (e: "close-modal"): void;
  (e: "generated"): void;
}>();

const { projectId } = storeToRefs(useAutoflowStore());

/** 자주 쓰는 Base Image 목록 (선택 또는 직접 입력) */
const BASE_IMAGE_PRESETS = [
  "python:3.10",
  "python:3.11",
  "python:3.12",
  "python:3.10-slim",
  "python:3.11-slim",
  "pytorch/pytorch:2.0.0-cuda11.7-cudnn8-runtime",
  "pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime",
  "tensorflow/tensorflow:2.14.0",
  "tensorflow/tensorflow:2.15.0",
  "nvidia/cuda:12.1.0-runtime-ubuntu22.04",
  "nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04",
];

const BASE_IMAGE_CUSTOM_VALUE = "__custom__";

/** Base Image: 목록 선택 값 또는 '__custom__'(직접 입력) */
const selectedBaseImage = ref("python:3.10");

function syncSelectedBaseImageFromForm() {
  const v = (form.value.base_image || "").trim();
  selectedBaseImage.value = BASE_IMAGE_PRESETS.includes(v) ? v : BASE_IMAGE_CUSTOM_VALUE;
}

function onBaseImageSelect(v: string) {
  if (v && v !== BASE_IMAGE_CUSTOM_VALUE) {
    form.value.base_image = v;
  }
}

// 학습용 입력 폼 (테스트용 샘플값)
const form = ref({
  base_image: "python:3.10",
  env: [
    { name: "WORKSPACE", value: "/workspace" },
    { name: "LOG_LEVEL", value: "info" },
  ] as { name: string; value: string }[],
  scriptFile: null as File[] | null,
  parameters: [
    { key: "model_name", value: "sample_model" },
    { key: "data_path", value: "/data" },
  ] as { key: string; value: string }[],
  // 스크립트 다운로드 방식: minio = MinIO 직접 접근, backend = Backend URL(curl)
  downloadMethod: "minio" as "minio" | "backend",
  // MinIO 직접 접근 시 Pod에서 접속할 주소 (비우면 업로드 응답의 endpoint 사용, 보통 localhost라 Pod에서 안 됨)
  minioEndpointPod: "http://minio-service.kubeflow.svc.cluster.local:9000",
  // MinIO 직접 접근 시 인증: "secret" = K8s Secret, "direct" = Access Key 직접 입력
  minioAuthMethod: "direct" as "secret" | "direct",
  minioSecretName: "mlpipeline-minio-artifact",
  // 직접 입력 시 (비우면 credentials env 미포함)
  minioAccessKey: "minioadmin",
  minioSecretKey: "minioadmin",
  // Backend URL (downloadMethod가 backend일 때만 사용)
  backendUrl: "http://autoflow-server-mgmt.default.svc.cluster.local:8080",
  // MLflow 사용 (체크 시 생성 YAML에 MLFLOW_TRACKING_URI 등 env 추가) — 로컬테스트 스크립트(kfp_mlflow_pipeline_v10_gpu_auth_local 등, WSL 환경)와 동일 기본값
  useMlflow: false,
  mlflowTrackingUri: "http://mlflow-server.kubeflow.svc.cluster.local:5000",
  mlflowExperimentName: "Default",
  mlflowUser: "user",
  mlflowPassword: "WjWjIi13KEkO",
  /** MinIO 인증: direct=직접 입력(minimal_gpu 등 다른 스크립트와 동일), secret=K8s 시크릿 이름 */
  mlflowMinioAuthMethod: "direct",
  /** K8s 시크릿 이름 (mlflowMinioAuthMethod가 secret일 때 사용) */
  mlflowMinioSecretName: "minio-mlflow-secret",
  /** 아티팩트 저장소(S3/MinIO). 직접 입력일 때 사용. minimal_gpu와 동일하게 minio-mlflow/minio-mlflow-12345. 업로드 직전 래퍼에서 AWS_*를 MLFLOW_*로 맞춤. */
  mlflowS3EndpointUrl: "http://minio-mlflow.kubeflow.svc.cluster.local:9000",
  mlflowAwsAccessKeyId: "minio-mlflow",
  mlflowAwsSecretAccessKey: "minio-mlflow-12345",
  // 자주 쓰는 파라미터 고정 필드
  epochs: "10",
  batch_size: "32",
  learning_rate: "0.001",
});

// 환경 변수 추가/삭제
function addEnv() {
  form.value.env.push({ name: "", value: "" });
}
function removeEnv(i: number) {
  form.value.env.splice(i, 1);
}

// 파라미터 추가/삭제
function addParameter() {
  form.value.parameters.push({ key: "", value: "" });
}
function removeParameter(i: number) {
  form.value.parameters.splice(i, 1);
}

// KFP v2 IR 파이프라인 YAML — 스크립트 없을 때 (이미지 내 경로 사용, 파일 없으면 실패)
const YAML_TEMPLATE_NO_SCRIPT = `# PIPELINE DEFINITION
# Name: auto-script-training
# Description: Auto Script generated training pipeline (KFP v2).
components:
  comp-train:
    executorLabel: exec-train
    inputDefinitions:
      parameters: {}
    outputDefinitions: {}
deploymentSpec:
  executors:
    exec-train:
      container:
        image: "{{ base_image }}"
{{ no_script_command_block }}
        env:
{{ env_yaml }}
pipelineInfo:
  name: auto-script-training
  description: Auto Script generated training pipeline (KFP v2).
root:
  dag:
    tasks:
      train:
        componentRef:
          name: comp-train
        taskInfo:
          name: train
  inputDefinitions:
    parameters: {}
schemaVersion: 2.1.0
sdkVersion: kfp-2.14.6
`;

// 스크립트 업로드 시: Pod에서 백엔드 URL로 curl 다운로드 후 실행
const YAML_TEMPLATE_WITH_SCRIPT = `# PIPELINE DEFINITION
# Name: auto-script-training
# Description: Auto Script generated training pipeline (KFP v2). Script downloaded at runtime.
components:
  comp-train:
    executorLabel: exec-train
    inputDefinitions:
      parameters: {}
    outputDefinitions: {}
deploymentSpec:
  executors:
    exec-train:
      container:
        image: "{{ base_image }}"
        command:
        - sh
        - -c
        - |
{{ script_command_body }}
        args:
{{ parameters_args }}
        env:
{{ script_env }}
{{ env_yaml }}
pipelineInfo:
  name: auto-script-training
  description: Auto Script generated training pipeline (KFP v2).
root:
  dag:
    tasks:
      train:
        componentRef:
          name: comp-train
        taskInfo:
          name: train
  inputDefinitions:
    parameters: {}
schemaVersion: 2.1.0
sdkVersion: kfp-2.14.6
`;

// 스크립트 업로드 시: Pod에서 MinIO에 직접 접근해 다운로드 후 실행 (curl/Backend 불필요)
// credentials는 command 내 export로 설정 (KFP가 container env를 적용하지 않는 경우 대비)
const YAML_TEMPLATE_MINIO_DIRECT = `# PIPELINE DEFINITION
# Name: auto-script-training
# Description: Auto Script (KFP v2). Script from MinIO direct.
components:
  comp-train:
    executorLabel: exec-train
    inputDefinitions:
      parameters: {}
    outputDefinitions: {}
deploymentSpec:
  executors:
    exec-train:
      container:
        image: "{{ base_image }}"
        command:
        - sh
        - -c
        - |
{{ minio_command_body }}
        args:
{{ parameters_args }}
        env:
{{ minio_script_env }}
{{ env_yaml }}
pipelineInfo:
  name: auto-script-training
  description: Auto Script (KFP v2). Script from MinIO direct.
root:
  dag:
    tasks:
      train:
        componentRef:
          name: comp-train
        taskInfo:
          name: train
  inputDefinitions:
    parameters: {}
schemaVersion: 2.1.0
sdkVersion: kfp-2.14.6
`;

const generatedYaml = ref("");
const errorMsg = ref("");
const saving = ref(false);
const scriptObjectName = ref(""); // 업로드 후 MinIO objectName (storagePath)
const scriptEntrypoint = ref("train.py"); // 실행할 파일명 (originalName)

// 폼 → KFP executor env 형식 (들여쓰기 8칸)
function envToYaml(): string {
  const list = form.value.env.filter((e) => (e.name || "").trim() !== "");
  if (list.length === 0) return "        []";
  return list
    .map(
      (e) =>
        `        - name: ${e.name}\n          value: "${String(e.value).replace(/"/g, '\\"')}"`
    )
    .join("\n");
}

/** MLflow 사용 시 스크립트에 MLflow 코드가 없어도 run/params/metric/artifact 자동 로깅. key=value 인자를 --key value 로 바꿔 argparse 호환. (single-quote 안에 넣을 코드라 작은따옴표 사용 금지) */
function getMlflowWrapperCode(): string {
  return [
    "import os, sys, json, runpy",
    "import mlflow",
    "uri = os.environ.get(\"MLFLOW_TRACKING_URI\")",
    "if uri: mlflow.set_tracking_uri(uri)",
    "exp = os.environ.get(\"MLFLOW_EXPERIMENT_NAME\")",
    "if exp: mlflow.set_experiment(exp)",
    "with mlflow.start_run():",
    "  kfp_run_id = os.environ.get(\"KFP_RUN_ID\", \"\").strip()",
    "  if kfp_run_id: mlflow.set_tag(\"kubeflow_run_id\", kfp_run_id)",
    "  print(\"[MLflow] KFP_RUN_ID=\", repr(kfp_run_id), \"run_id=\", mlflow.active_run().info.run_id if mlflow.active_run() else None, flush=True)",
    "  raw = sys.argv[2:]",
    "  for arg in raw:",
    "    if \"=\" in arg:",
    "      k, v = arg.split(\"=\", 1)",
    "      mlflow.log_param(k, v)",
    "  argv = [sys.argv[1]]",
    "  for arg in raw:",
    "    if \"=\" in arg:",
    "      k, v = arg.split(\"=\", 1)",
    "      argv.extend([\"--\" + k, v])",
    "  sys.argv = argv",
    "  runpy.run_path(sys.argv[0], run_name=\"__main__\")",
    "  for p in [\"/workspace/outputs/metrics.json\", \"/workspace/metrics.json\"]:",
    "    if os.path.exists(p):",
    "      try:",
    "        with open(p) as f: d = json.load(f)",
    "        for k, v in d.items():",
    "          if isinstance(v, (int, float)): mlflow.log_metric(k, v)",
    "      except Exception: pass",
    "      break",
    "  try:",
    "    if os.path.isdir(\"/workspace/outputs\"):",
    "      ak = os.environ.get(\"MLFLOW_AWS_ACCESS_KEY_ID\") or os.environ.get(\"AWS_ACCESS_KEY_ID\", \"\")",
    "      sk = os.environ.get(\"MLFLOW_AWS_SECRET_ACCESS_KEY\") or os.environ.get(\"AWS_SECRET_ACCESS_KEY\", \"\")",
    "      if ak: os.environ[\"AWS_ACCESS_KEY_ID\"] = ak",
    "      if sk: os.environ[\"AWS_SECRET_ACCESS_KEY\"] = sk",
    "      mlflow.log_artifacts(\"/workspace/outputs\", artifact_path=\"outputs\")",
    "      print(\"[MLflow] artifacts uploaded from /workspace/outputs\", flush=True)",
    "  except Exception as e:",
    "    print(\"[MLflow] artifact upload failed:\", e, flush=True)",
  ].join("\n");
}

/** run 명령: useWrapper면 MLflow 래퍼로 실행(파라미터/아티팩트 자동 로깅), 아니면 exec python만 */
function getMlflowRunCommand(scriptPath: string, useWrapper: boolean): string {
  if (!useWrapper) return "exec python " + scriptPath + ' "$@"';
  const code = getMlflowWrapperCode();
  const indented = code.split("\n").map((l) => "          " + l).join("\n");
  return "python -c '\n" + indented + "\n          ' " + scriptPath + ' "$@"';
}

/** MLflow 사용 시 env에 추가할 YAML 블록 (들여쓰기 8칸) */
function mlflowEnvToYaml(): string {
  if (!form.value.useMlflow) return "";
  const uri = (form.value.mlflowTrackingUri || "").trim();
  if (!uri) return "";
  const lines: string[] = [];
  lines.push(`        - name: MLFLOW_TRACKING_URI\n          value: "${String(uri).replace(/"/g, '\\"')}"`);
  const exp = (form.value.mlflowExperimentName || "").trim();
  if (exp) lines.push(`        - name: MLFLOW_EXPERIMENT_NAME\n          value: "${String(exp).replace(/"/g, '\\"')}"`);
  const user = (form.value.mlflowUser || "").trim();
  if (user) lines.push(`        - name: MLFLOW_TRACKING_USERNAME\n          value: "${String(user).replace(/"/g, '\\"')}"`);
  const pass = (form.value.mlflowPassword || "").trim();
  if (pass) lines.push(`        - name: MLFLOW_TRACKING_PASSWORD\n          value: "${String(pass).replace(/"/g, '\\"')}"`);
  lines.push("        - name: KFP_RUN_ID\n          value: \"{{workflow.uid}}\"");
  // 아티팩트: S3 엔드포인트는 공통, 자격증명은 직접 입력 또는 K8s 시크릿 참조
  const s3Endpoint = (form.value.mlflowS3EndpointUrl || "").trim() || "http://minio-mlflow.kubeflow.svc.cluster.local:9000";
  lines.push(`        - name: MLFLOW_S3_ENDPOINT_URL\n          value: "${String(s3Endpoint).replace(/"/g, '\\"')}"`);
  if (form.value.mlflowMinioAuthMethod === "secret") {
    const secretName = (form.value.mlflowMinioSecretName || "").trim() || "minio-mlflow-secret";
    lines.push(`        - name: MLFLOW_AWS_ACCESS_KEY_ID\n          valueFrom:\n            secretKeyRef:\n              name: ${secretName}\n              key: accesskey`);
    lines.push(`        - name: MLFLOW_AWS_SECRET_ACCESS_KEY\n          valueFrom:\n            secretKeyRef:\n              name: ${secretName}\n              key: secretkey`);
  } else {
    const s3Key = (form.value.mlflowAwsAccessKeyId || "").trim() || "minio-mlflow";
    const s3Secret = (form.value.mlflowAwsSecretAccessKey || "").trim() || "minio-mlflow-12345";
    lines.push(`        - name: MLFLOW_AWS_ACCESS_KEY_ID\n          value: "${String(s3Key).replace(/"/g, '\\"')}"`);
    lines.push(`        - name: MLFLOW_AWS_SECRET_ACCESS_KEY\n          value: "${String(s3Secret).replace(/"/g, '\\"')}"`);
  }
  return lines.length > 0 ? lines.join("\n") : "";
}

// 폼 → executor args (key=value 리스트, 들여쓰기 8칸)
function parametersToArgs(): string {
  const fixed: Record<string, string> = {
    epochs: form.value.epochs,
    batch_size: form.value.batch_size,
    learning_rate: form.value.learning_rate,
  };
  const extra = form.value.parameters.filter((p) => (p.key || "").trim() !== "");
  const merged = { ...fixed };
  extra.forEach((p) => (merged[p.key] = p.value));
  const entries = Object.entries(merged).filter(([, v]) => v !== undefined && v !== "");
  if (entries.length === 0) return "        []";
  return entries.map(([k, v]) => `        - "${k}=${v}"`).join("\n");
}

async function generateYaml() {
  errorMsg.value = "";
  try {
    const base_image = (form.value.base_image || "python:3.10").trim();
    const env_yaml = envToYaml();
    const parameters_args = parametersToArgs();
    const fileObj = Array.isArray(form.value.scriptFile)
      ? form.value.scriptFile[0]
      : form.value.scriptFile;
    const backendUrl = (form.value.backendUrl || "").trim();
    const downloadMethod = form.value.downloadMethod || "minio";

    if (fileObj && projectId.value) {
      const regUserId = getRegUserId();
      const originalName = fileObj.name || "train.py";
      // 파이썬 파일은 원본 이름 그대로 저장 → entrypoint가 train.py 등으로 맞아서 파이프라인에서 로드 가능
      const fd = new FormData();
      fd.append("refId", String(props.currentRefId ?? 0));
      fd.append("refType", "TRAINING_SCRIPT");
      fd.append("title", "Script - " + originalName);
      fd.append("description", "Auto Script 학습용 스크립트");
      fd.append("version", "1");
      fd.append("regUserId", regUserId);
      fd.append("projectId", String(projectId.value));
      fd.append("file", fileObj, originalName);
      const res = await AttachmentsService.upload(fd as any);
      const data = res?.data ?? res;
        const attachment = data?.attachment ?? data;
        const storagePath = attachment?.storagePath ?? attachment?.storage_path;
        const entrypointName = attachment?.originalName ?? attachment?.original_name ?? originalName ?? "train.py";
        if (!storagePath) {
          errorMsg.value = "스크립트 업로드 후 저장 경로를 가져오지 못했습니다.";
          return;
        }
        scriptObjectName.value = storagePath;
        scriptEntrypoint.value = entrypointName;

      const envBase = form.value.env.filter((e) => (e.name || "").trim() !== "").length > 0 ? env_yaml : "";
      const mlflowBlock = mlflowEnvToYaml();
      const env_yaml_extra = [envBase, mlflowBlock].filter(Boolean).join("\n") || "        []";
      const needMlflowInstall = !!(form.value.useMlflow && (form.value.mlflowTrackingUri || "").trim());

      if (downloadMethod === "minio") {
        const minioBucket = (data?.minioBucket ?? data?.minio_bucket ?? "").trim();
        const endpointFromBackend = (data?.minioEndpoint ?? data?.minio_endpoint ?? "").trim();
        const endpointPod = (form.value.minioEndpointPod ?? "").trim();
        const minioEndpoint = endpointPod || endpointFromBackend;
        if (!minioBucket) {
          errorMsg.value = "업로드 응답에 MinIO bucket이 없습니다. 백엔드 설정을 확인하세요.";
          return;
        }
        if (!minioEndpoint) {
          errorMsg.value = "MinIO Endpoint(Pod용)를 입력하거나, 업로드 응답에 minioEndpoint가 있어야 합니다.";
          return;
        }
        let minio_script_env =
          `        - name: S3_ENDPOINT\n          value: "${String(minioEndpoint).replace(/"/g, '\\"')}"\n` +
          `        - name: S3_BUCKET\n          value: "${String(minioBucket).replace(/"/g, '\\"')}"\n` +
          `        - name: SCRIPT_OBJECT_KEY\n          value: "${String(storagePath).replace(/"/g, '\\"')}"\n`;
        const authMethod = form.value.minioAuthMethod || "direct";
        const directAccessKey = (form.value.minioAccessKey ?? "").trim();
        const directSecretKey = (form.value.minioSecretKey ?? "").trim();
        const secretName = (form.value.minioSecretName || "").trim();
        const ak = directAccessKey || "minioadmin";
        const sk = directSecretKey || "minioadmin";
        if (authMethod === "secret" && secretName) {
          minio_script_env +=
            `        - name: AWS_ACCESS_KEY_ID\n          valueFrom:\n            secretKeyRef:\n              name: ${secretName}\n              key: accesskey\n` +
            `        - name: AWS_SECRET_ACCESS_KEY\n          valueFrom:\n            secretKeyRef:\n              name: ${secretName}\n              key: secretkey\n`;
        } else {
          minio_script_env +=
            `        - name: AWS_ACCESS_KEY_ID\n          value: "${String(ak).replace(/"/g, '\\"')}"\n` +
            `        - name: AWS_SECRET_ACCESS_KEY\n          value: "${String(sk).replace(/"/g, '\\"')}"\n`;
        }
        // command 내 export용: 셸 single-quote 안에서 쓰이므로 ' -> '\''
        const shellEsc = (s: string) => String(s).replace(/'/g, "'\\''");
        const ep = scriptEntrypoint.value;
        const scriptPathMinio = "/workspace/" + ep;
        const minio_command_body =
          "          set -e\n" +
          "          export AWS_ACCESS_KEY_ID='" + shellEsc(ak) + "'\n" +
          "          export AWS_SECRET_ACCESS_KEY='" + shellEsc(sk) + "'\n" +
          "          pip install -q awscli\n" +
          "          aws s3 cp s3://$S3_BUCKET/$SCRIPT_OBJECT_KEY " + scriptPathMinio + " --endpoint-url $S3_ENDPOINT\n" +
          (needMlflowInstall ? "          echo 'numpy<2' > /tmp/constraints.txt && pip install -q -c /tmp/constraints.txt \"numpy>=1.24,<2\" \"pyarrow>=13,<15\" mlflow boto3\n          " : "") +
          "          " + getMlflowRunCommand(scriptPathMinio, needMlflowInstall);
        let yaml = YAML_TEMPLATE_MINIO_DIRECT.replace(/\{\{\s*base_image\s*\}\}/g, base_image)
          .replace(/\{\{\s*env_yaml\s*\}\}/g, env_yaml_extra)
          .replace(/\{\{\s*parameters_args\s*\}\}/g, parameters_args)
          .replace(/\{\{\s*entrypoint\s*\}\}/g, ep)
          .replace(/\{\{\s*minio_script_env\s*\}\}/g, minio_script_env)
          .replace(/\{\{\s*minio_command_body\s*\}\}/g, minio_command_body);
        generatedYaml.value = yaml;
      } else {
        if (!backendUrl) {
          errorMsg.value = "Backend URL 방식을 사용하려면 'Backend URL'을 입력하세요.";
          return;
        }
        const script_env =
          `        - name: SCRIPT_OBJECT_NAME\n          value: "${String(storagePath).replace(/"/g, '\\"')}"\n        - name: BACKEND_URL\n          value: "${String(backendUrl).replace(/"/g, '\\"')}"\n`;
        const epBackend = scriptEntrypoint.value;
        const scriptPathBackend = "/workspace/" + epBackend;
        const script_command_body =
          "          set -e\n" +
          '          curl -s -o ' + scriptPathBackend + ' "$BACKEND_URL/api/attachments/download?objectName=$SCRIPT_OBJECT_NAME"\n' +
          (needMlflowInstall ? "          echo 'numpy<2' > /tmp/constraints.txt && pip install -q -c /tmp/constraints.txt \"numpy>=1.24,<2\" \"pyarrow>=13,<15\" mlflow boto3\n          " : "") +
          "          " + getMlflowRunCommand(scriptPathBackend, needMlflowInstall);
        let yaml = YAML_TEMPLATE_WITH_SCRIPT.replace(/\{\{\s*base_image\s*\}\}/g, base_image)
          .replace(/\{\{\s*env_yaml\s*\}\}/g, env_yaml_extra)
          .replace(/\{\{\s*parameters_args\s*\}\}/g, parameters_args)
          .replace(/\{\{\s*entrypoint\s*\}\}/g, epBackend)
          .replace(/\{\{\s*script_env\s*\}\}/g, script_env)
          .replace(/\{\{\s*script_command_body\s*\}\}/g, script_command_body);
        generatedYaml.value = yaml;
      }
    } else {
      const entrypoint = scriptEntrypoint.value || "train.py";
      if (fileObj && !projectId.value) {
        errorMsg.value = "스크립트를 사용하려면 프로젝트를 선택하세요.";
        return;
      }
      const noScriptEnvBase = envToYaml();
      const noScriptMlflow = mlflowEnvToYaml();
      const noScriptEnv = [noScriptEnvBase, noScriptMlflow].filter(Boolean).join("\n") || "        []";
      const needMlflowInstallNoScript = !!(form.value.useMlflow && (form.value.mlflowTrackingUri || "").trim());
      const noScriptRunCmd = getMlflowRunCommand(entrypoint, needMlflowInstallNoScript);
      const no_script_command_block = needMlflowInstallNoScript
        ? "        command:\n        - sh\n        - -c\n        - |\n          set -e\n          echo 'numpy<2' > /tmp/constraints.txt && pip install -q -c /tmp/constraints.txt \"numpy>=1.24,<2\" \"pyarrow>=13,<15\" mlflow boto3\n          " + noScriptRunCmd + "\n        args:\n" + parameters_args
        : "        command:\n        - python\n        - \"" + entrypoint + "\"\n        args:\n" + parameters_args;
      let yaml = YAML_TEMPLATE_NO_SCRIPT.replace(/\{\{\s*base_image\s*\}\}/g, base_image)
        .replace(/\{\{\s*env_yaml\s*\}\}/g, noScriptEnv)
        .replace(/\{\{\s*parameters_args\s*\}\}/g, parameters_args)
        .replace(/\{\{\s*entrypoint\s*\}\}/g, entrypoint)
        .replace(/\{\{\s*no_script_command_block\s*\}\}/g, no_script_command_block);
      generatedYaml.value = yaml;
    }
  } catch (e: any) {
    console.error(e);
    const err = e as { response?: { data?: { error?: string }; status?: number }; message?: string };
    const backendError = err.response?.data?.error;
    if (backendError) {
      errorMsg.value = "스크립트 업로드 실패: " + backendError;
    } else {
      errorMsg.value = "YAML 생성 중 오류가 발생했습니다. " + (err?.message ?? String(e));
    }
  }
}

function downloadYaml() {
  if (!generatedYaml.value) {
    errorMsg.value = "먼저 [YAML 생성]을 실행해주세요.";
    return;
  }
  const blob = new Blob([generatedYaml.value], { type: "text/yaml" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = `training-pipeline_${getTimestampForFileName()}.yaml`;
  a.click();
  URL.revokeObjectURL(url);
}

async function saveAsTrainingScript() {
  if (!generatedYaml.value) {
    errorMsg.value = "먼저 [YAML 생성]을 실행해주세요.";
    return;
  }
  if (!projectId.value) {
    errorMsg.value = "프로젝트를 선택해주세요.";
    return;
  }
  errorMsg.value = "";
  try {
    saving.value = true;
    const ts = getTimestampForFileName();
    const yamlFileName = `training-pipeline_${ts}.yaml`;
    const blob = new Blob([generatedYaml.value], { type: "text/yaml" });
    const file = new File([blob], yamlFileName, { type: "text/yaml" });
    const fd = new FormData();
    const regUserId = getRegUserId();
    fd.append("refId", String(props.currentRefId ?? 0));
    fd.append("refType", "TRAINING_SCRIPT");
    fd.append("title", "Auto Script - " + yamlFileName);
    fd.append("description", "Auto Script로 생성된 학습 YAML");
    fd.append("version", "1");
    fd.append("regUserId", regUserId);
    fd.append("projectId", String(projectId.value));
    fd.append("file", file);
    await AttachmentsService.upload(fd as any);
    emit("generated");
    emit("close-modal");
  } catch (e) {
    console.error(e);
    errorMsg.value = "Training Script 저장에 실패했습니다.";
  } finally {
    saving.value = false;
  }
}

/** 파일명에 쓸 일시 문자열 (예: 20260303_143052) */
function getTimestampForFileName(): string {
  const now = new Date();
  const y = now.getFullYear();
  const m = String(now.getMonth() + 1).padStart(2, "0");
  const d = String(now.getDate()).padStart(2, "0");
  const h = String(now.getHours()).padStart(2, "0");
  const min = String(now.getMinutes()).padStart(2, "0");
  const s = String(now.getSeconds()).padStart(2, "0");
  return `${y}${m}${d}_${h}${min}${s}`;
}

function getRegUserId(): string {
  try {
    const raw = localStorage.getItem("autoflow-auth") || "{}";
    const auth = JSON.parse(raw);
    return (
      auth?.userInfo?.username ??
      auth?.userinfo?.username ??
      auth?.username ??
      ""
    );
  } catch {
    return "";
  }
}

function onEsc(e: KeyboardEvent) {
  if (e.key === "Escape") emit("close-modal");
}

/** 스크립트 저장 시 YAML에 반영할 MinIO 설정을 백엔드에서 불러와 폼에 채움 (직접 입력 불필요) */
async function loadMinioConfig() {
  try {
    const res = await AttachmentsService.getMinioConfig();
    const data = (res?.data ?? res) as Record<string, string> | undefined;
    if (!data) return;
    if ((data.minioEndpointPod ?? "").trim()) form.value.minioEndpointPod = (data.minioEndpointPod ?? "").trim();
    if ((data.minioAccessKey ?? "").trim()) form.value.minioAccessKey = (data.minioAccessKey ?? "").trim();
    if ((data.minioSecretKey ?? "").trim()) form.value.minioSecretKey = (data.minioSecretKey ?? "").trim();
  } catch {
    // 설정 API 없거나 실패 시 폼 기본값 유지
  }
}

/** MLflow 설정을 백엔드에서 불러와 폼에 채움. localhost는 Pod에서 접근 불가하므로 덮어쓰지 않음 */
async function loadMlflowConfig() {
  try {
    const res = await AttachmentsService.getMlflowConfig();
    const data = (res?.data ?? res) as Record<string, string> | undefined;
    if (!data) return;
    const uri = (data.mlflowTrackingUri ?? "").trim();
    if (uri && !/^https?:\/\/localhost(\b|:)/i.test(uri)) form.value.mlflowTrackingUri = uri;
    if ((data.mlflowUser ?? "").trim()) form.value.mlflowUser = (data.mlflowUser ?? "").trim();
    if ((data.mlflowPassword ?? "").trim()) form.value.mlflowPassword = (data.mlflowPassword ?? "").trim();
  } catch {
    // 설정 API 없거나 실패 시 폼 기본값 유지
  }
}

onMounted(() => {
  addEnv();
  addParameter();
  syncSelectedBaseImageFromForm();
  loadMinioConfig();
  loadMlflowConfig();
  window.addEventListener("keydown", onEsc);
});
onBeforeUnmount(() => window.removeEventListener("keydown", onEsc));
</script>

<template>
  <v-card class="rounded-lg overflow-hidden">
    <v-card-title
      class="text-white font-weight-bold text-h6"
      style="background-color: #1976d2"
    >
      Auto Script — KFP 학습용 파이프라인 YAML 생성
    </v-card-title>

    <v-card-text class="pa-6" style="max-height: 70vh; overflow-y: auto">
      <v-form>
        <!-- Base Image: 목록 선택 또는 직접 입력 -->
        <div class="mb-4">
          <label class="text-subtitle-2 font-weight-medium mb-1 d-block"
            >Base Image</label
          >
          <v-select
            v-model="selectedBaseImage"
            :items="[
              ...BASE_IMAGE_PRESETS.map((t) => ({ title: t, value: t })),
              { title: '직접 입력', value: BASE_IMAGE_CUSTOM_VALUE },
            ]"
            item-title="title"
            item-value="value"
            variant="outlined"
            density="compact"
            hide-details
            @update:model-value="onBaseImageSelect"
          />
          <v-text-field
            v-if="selectedBaseImage === BASE_IMAGE_CUSTOM_VALUE"
            v-model="form.base_image"
            variant="outlined"
            density="compact"
            hide-details
            placeholder="예: python:3.10, my-registry.io/my-image:tag"
            class="mt-2"
          />
        </div>

        <!-- 환경 변수 -->
        <div class="mb-4">
          <div class="d-flex align-center justify-space-between mb-2">
            <label class="text-subtitle-2 font-weight-medium"
              >환경 변수</label
            >
            <v-btn size="small" variant="tonal" @click="addEnv"
              >+ 추가</v-btn
            >
          </div>
          <div
            v-for="(row, i) in form.env"
            :key="i"
            class="d-flex align-center gap-2 mb-2"
          >
            <v-text-field
              v-model="row.name"
              variant="outlined"
              density="compact"
              hide-details
              placeholder="NAME"
              class="flex-grow-1"
            />
            <v-text-field
              v-model="row.value"
              variant="outlined"
              density="compact"
              hide-details
              placeholder="value"
              class="flex-grow-1"
            />
            <v-btn
              icon
              size="small"
              variant="text"
              color="error"
              @click="removeEnv(i)"
            >
              <v-icon>mdi-minus</v-icon>
            </v-btn>
          </div>
        </div>

        <!-- 학습 스크립트 업로드 (선택) -->
        <div class="mb-4">
          <label class="text-subtitle-2 font-weight-medium mb-1 d-block"
            >학습 스크립트 (선택) — train.py 등</label
          >
          <v-file-input
            v-model="form.scriptFile"
            label="파일 선택"
            variant="outlined"
            density="compact"
            hide-details
            accept=".py,.sh"
            clearable
          />
          <p class="text-caption text-medium-emphasis mt-1 mb-0">
            선택 시 스크립트는 MinIO에 업로드됩니다. 아래 방식 중 하나로 Pod가 스크립트를 받아옵니다.
          </p>
        </div>

        <!-- MLflow 사용 -->
        <div class="mb-4">
          <v-checkbox
            v-model="form.useMlflow"
            label="MLflow 사용 (생성 YAML에 MLFLOW_TRACKING_URI 등 env 추가)"
            density="compact"
            hide-details
            class="mt-0"
          />
          <template v-if="form.useMlflow">
            <v-text-field
              v-model="form.mlflowTrackingUri"
              label="MLflow Tracking URI"
              variant="outlined"
              density="compact"
              hide-details
              placeholder="http://mlflow-server.kubeflow.svc.cluster.local:5000"
              class="mt-2"
            />
            <v-text-field
              v-model="form.mlflowExperimentName"
              label="MLflow Experiment Name (선택)"
              variant="outlined"
              density="compact"
              hide-details
              placeholder="Default"
              class="mt-2"
            />
            <v-text-field
              v-model="form.mlflowUser"
              label="MLflow Username (Basic Auth 사용 시에만)"
              variant="outlined"
              density="compact"
              hide-details
              placeholder="admin"
              class="mt-2"
            />
            <v-text-field
              v-model="form.mlflowPassword"
              label="MLflow Password (Basic Auth 사용 시에만)"
              type="password"
              variant="outlined"
              density="compact"
              hide-details
              placeholder="비밀번호 입력"
              class="mt-2"
            />
            <v-radio-group v-model="form.mlflowMinioAuthMethod" hide-details density="compact" class="mt-2">
              <v-radio label="K8s 시크릿 이름 (MLflow 아티팩트용)" value="secret" />
              <v-radio label="직접 입력 (기본값 사용)" value="direct" />
            </v-radio-group>
            <v-text-field
              v-if="form.mlflowMinioAuthMethod === 'secret'"
              v-model="form.mlflowMinioSecretName"
              label="아티팩트 시크릿 이름"
              variant="outlined"
              density="compact"
              hide-details
              placeholder="minio-mlflow-secret"
              class="mt-2"
            />
            <template v-if="form.mlflowMinioAuthMethod === 'direct'">
              <v-text-field
                v-model="form.mlflowAwsAccessKeyId"
                label="아티팩트 Access Key"
                variant="outlined"
                density="compact"
                hide-details
                class="mt-2"
              />
              <v-text-field
                v-model="form.mlflowAwsSecretAccessKey"
                label="아티팩트 Secret Key"
                type="password"
                variant="outlined"
                density="compact"
                hide-details
                class="mt-2"
              />
            </template>
            <v-text-field
              v-model="form.mlflowS3EndpointUrl"
              label="아티팩트 S3 엔드포인트"
              variant="outlined"
              density="compact"
              hide-details
              placeholder="http://minio-mlflow.kubeflow.svc.cluster.local:9000"
              class="mt-2"
            />
            <p class="text-caption text-medium-emphasis mt-1 mb-0">
              다이얼로그 열 때 서버 설정에서 불러옵니다. Pod에서 접근 가능한 URI를 입력하세요.
            </p>
          </template>
        </div>

        <!-- 스크립트 다운로드 방식 -->
        <div class="mb-4">
          <label class="text-subtitle-2 font-weight-medium mb-2 d-block"
            >스크립트 다운로드 방식</label
          >
          <v-radio-group v-model="form.downloadMethod" hide-details density="compact" class="mt-0">
            <v-radio label="MinIO 직접 접근 (권장)" value="minio" />
            <v-radio label="Backend URL (curl)" value="backend" />
          </v-radio-group>
          <p class="text-caption text-medium-emphasis mt-1 mb-0">
            MinIO 직접 접근: Pod가 저장소(MinIO)에 바로 접속해 받습니다. Backend URL 불필요.
          </p>
        </div>

        <!-- MinIO Endpoint Pod용 (MinIO 직접 접근 시) -->
        <div v-if="form.downloadMethod === 'minio'" class="mb-4">
          <label class="text-subtitle-2 font-weight-medium mb-1 d-block"
            >MinIO Endpoint (Pod에서 접속할 주소)</label
          >
          <v-text-field
            v-model="form.minioEndpointPod"
            variant="outlined"
            density="compact"
            hide-details
            placeholder="http://minio-service.kubeflow.svc.cluster.local:9000"
          />
          <p class="text-caption text-medium-emphasis mt-1 mb-0">
            다이얼로그 열 때 서버 설정(minio.endpoint.pod)에서 불러옵니다. 필요 시 수정하세요.
          </p>
        </div>

        <!-- MinIO 인증 (MinIO 직접 접근 시) -->
        <div v-if="form.downloadMethod === 'minio'" class="mb-4">
          <label class="text-subtitle-2 font-weight-medium mb-2 d-block"
            >MinIO 인증</label
          >
          <v-radio-group v-model="form.minioAuthMethod" hide-details density="compact" class="mt-0 mb-2">
            <v-radio label="직접 입력 (Access Key / Secret Key)" value="direct" />
            <v-radio label="K8s 시크릿 이름 (스크립트 다운로드용)" value="secret" />
          </v-radio-group>
          <template v-if="form.minioAuthMethod === 'direct'">
            <v-text-field
              v-model="form.minioAccessKey"
              label="MinIO Access Key"
              variant="outlined"
              density="compact"
              hide-details
              class="mb-2"
              placeholder="minioadmin"
            />
            <v-text-field
              v-model="form.minioSecretKey"
              label="MinIO Secret Key"
              type="password"
              variant="outlined"
              density="compact"
              hide-details
              placeholder="minioadmin"
            />
            <p class="text-caption text-medium-emphasis mt-1 mb-0">
              다이얼로그 열 때 서버 설정에서 불러와 YAML에 반영됩니다. 필요 시 수정하세요.
            </p>
          </template>
          <template v-else>
            <v-text-field
              v-model="form.minioSecretName"
              label="시크릿 이름 (스크립트 다운로드)"
              variant="outlined"
              density="compact"
              hide-details
              placeholder="mlpipeline-minio-artifact"
            />
            <p class="text-caption text-medium-emphasis mt-1 mb-0">
              KFP 기본: mlpipeline-minio-artifact (키: accesskey, secretkey). 실행 네임스페이스에 시크릿이 있어야 합니다.
            </p>
          </template>
        </div>

        <!-- Backend URL (Backend URL 방식일 때만) -->
        <div v-if="form.downloadMethod === 'backend'" class="mb-4">
          <label class="text-subtitle-2 font-weight-medium mb-1 d-block"
            >Backend URL (스크립트 다운로드용)</label
          >
          <v-text-field
            v-model="form.backendUrl"
            variant="outlined"
            density="compact"
            hide-details
            placeholder="http://autoflow-server-mgmt.default.svc.cluster.local:8080"
          />
          <p class="text-caption text-medium-emphasis mt-1 mb-0">
            Pod가 이 주소로 스크립트를 받아옵니다. 클러스터 내 접근 가능한 백엔드 주소를 입력하세요.
          </p>
        </div>

        <!-- 고정 파라미터 -->
        <div class="mb-4">
          <label class="text-subtitle-2 font-weight-medium mb-2 d-block"
            >학습 파라미터</label
          >
          <v-row dense>
            <v-col cols="4">
              <v-text-field
                v-model="form.epochs"
                variant="outlined"
                density="compact"
                hide-details
                label="epochs"
              />
            </v-col>
            <v-col cols="4">
              <v-text-field
                v-model="form.batch_size"
                variant="outlined"
                density="compact"
                hide-details
                label="batch_size"
              />
            </v-col>
            <v-col cols="4">
              <v-text-field
                v-model="form.learning_rate"
                variant="outlined"
                density="compact"
                hide-details
                label="learning_rate"
              />
            </v-col>
          </v-row>
        </div>

        <!-- 추가 파라미터 (키-값) -->
        <div class="mb-4">
          <div class="d-flex align-center justify-space-between mb-2">
            <label class="text-subtitle-2 font-weight-medium"
              >추가 파라미터</label
            >
            <v-btn size="small" variant="tonal" @click="addParameter"
              >+ 추가</v-btn
            >
          </div>
          <div
            v-for="(row, i) in form.parameters"
            :key="i"
            class="d-flex align-center gap-2 mb-2"
          >
            <v-text-field
              v-model="row.key"
              variant="outlined"
              density="compact"
              hide-details
              placeholder="key"
              class="flex-grow-1"
            />
            <v-text-field
              v-model="row.value"
              variant="outlined"
              density="compact"
              hide-details
              placeholder="value"
              class="flex-grow-1"
            />
            <v-btn
              icon
              size="small"
              variant="text"
              color="error"
              @click="removeParameter(i)"
            >
              <v-icon>mdi-minus</v-icon>
            </v-btn>
          </div>
        </div>

        <!-- YAML 생성 버튼 -->
        <div class="mb-3">
          <v-btn color="primary" @click="generateYaml">YAML 생성</v-btn>
        </div>

        <!-- 생성된 YAML 미리보기 -->
        <div v-if="generatedYaml" class="mb-3">
          <label class="text-subtitle-2 font-weight-medium mb-1 d-block"
            >생성된 YAML</label
          >
          <v-textarea
            :model-value="generatedYaml"
            variant="outlined"
            readonly
            rows="12"
            hide-details
            class="mono"
          />
        </div>

        <div v-if="errorMsg" class="text-error mb-2">{{ errorMsg }}</div>
      </v-form>
    </v-card-text>

    <v-card-actions class="justify-end flex-wrap" style="padding: 16px 24px">
      <v-btn
        v-if="generatedYaml"
        color="success"
        variant="tonal"
        :disabled="saving"
        @click="downloadYaml"
      >
        다운로드
      </v-btn>
      <v-btn
        v-if="generatedYaml"
        color="success"
        :loading="saving"
        @click="saveAsTrainingScript"
      >
        Training Script로 저장
      </v-btn>
      <v-btn variant="text" :disabled="saving" @click="emit('close-modal')">
        닫기
      </v-btn>
    </v-card-actions>
  </v-card>
</template>

<style scoped>
.mono {
  font-family: ui-monospace, monospace;
  font-size: 0.85em;
}
</style>
