package com.kamco.cd.training.train.service;

import com.kamco.cd.training.train.dto.ModelTrainLinkDto;
import java.io.IOException;
import java.nio.file.*;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Slf4j
@Service
@RequiredArgsConstructor
public class TmpDatasetService {

  @Value("${train.docker.request_dir}")
  private String requestDir;

  @Value("${train.docker.symbolic_link_dir}")
  private String symbolicDir;

  /**
   * train, val, test 폴더별로 link
   *
   * @param uid 임시폴더 uuid
   * @param type train, val, test
   * @param links tif pull path
   * @return
   * @throws IOException
   */
  public void buildTmpDatasetHardlink(String uid, String type, List<ModelTrainLinkDto> links)
      throws IOException {

    if (links == null || links.isEmpty()) {
      throw new IOException("links is empty");
    }

    Path tmp = Path.of(symbolicDir, uid);

    long linksMade = 0;

    for (ModelTrainLinkDto dto : links) {

      if (type == null) {
        log.warn("SKIP - trainType null: {}", dto);
        continue;
      }

      // type별 디렉토리 생성
      Files.createDirectories(tmp.resolve(type).resolve("input1"));
      Files.createDirectories(tmp.resolve(type).resolve("input2"));
      Files.createDirectories(tmp.resolve(type).resolve("label"));
      Files.createDirectories(tmp.resolve(type).resolve("label-json"));

      // comparePath → input1
      linksMade += link(tmp, type, "input1", dto.getComparePath());

      // targetPath → input2
      linksMade += link(tmp, type, "input2", dto.getTargetPath());

      // labelPath → label
      linksMade += link(tmp, type, "label", dto.getLabelPath());

      // geoJsonPath -> label-json
      linksMade += link(tmp, type, "label-json", dto.getGeoJsonPath());
    }

    if (linksMade == 0) {
      throw new IOException("No symlinks created.");
    }

    log.info("tmp dataset created: {}, linksMade={}", tmp, linksMade);
  }

  private long link(Path tmp, String type, String part, String fullPath) throws IOException {
    if (fullPath == null || fullPath.isBlank()) return 0;

    Path src = Path.of(fullPath);

    if (!Files.isRegularFile(src)) {
      log.warn("SKIP (not file): {}", src);
      return 0;
    }

    String fileName = src.getFileName().toString();
    Path dst = tmp.resolve(type).resolve(part).resolve(fileName);

    Files.createDirectories(dst.getParent());

    if (Files.exists(dst) || Files.isSymbolicLink(dst)) {
      Files.delete(dst);
    }

    Files.createSymbolicLink(dst, src);
    log.info("symlink created: {} -> {}", dst, src);

    return 1;
  }

  // TODO 미사용시작
  /**
   * request 전체 폴더 link
   *
   * @param uid
   * @param datasetUids
   * @return
   * @throws IOException
   */
  public String buildTmpDatasetSymlink(String uid, List<String> datasetUids) throws IOException {

    log.info("========== buildTmpDatasetSymlink START ==========");
    log.info("uid={}", uid);
    log.info("datasetUids={}", datasetUids);
    log.info("requestDir(raw)={}", requestDir);

    Path BASE = toPath(requestDir);
    Path tmp = Path.of(symbolicDir, uid);

    log.info("BASE={}", BASE);
    log.info("BASE exists? {}", Files.isDirectory(BASE));
    log.info("tmp={}", tmp);

    long noDir = 0, scannedDirs = 0, regularFiles = 0, symlinksMade = 0;

    // tmp 디렉토리 준비
    for (String type : List.of("train", "val", "test")) {
      for (String part : List.of("input1", "input2", "label", "label-json")) {
        Path dir = tmp.resolve(type).resolve(part);
        Files.createDirectories(dir);
        log.info("createDirectories: {}", dir);
      }
    }

    // 심볼릭 링크는 파일시스템이 달라도 작동하므로 FileStore 체크 불필요

    for (String id : datasetUids) {
      Path srcRoot = BASE.resolve(id);
      log.info("---- dataset id={} srcRoot={} exists? {}", id, srcRoot, Files.isDirectory(srcRoot));

      for (String type : List.of("train", "val", "test")) {
        for (String part : List.of("input1", "input2", "label", "label-json")) {

          Path srcDir = srcRoot.resolve(type).resolve(part);
          if (!Files.isDirectory(srcDir)) {
            log.warn("SKIP (not directory): {}", srcDir);
            noDir++;
            continue;
          }

          scannedDirs++;
          log.info("SCAN dir={}", srcDir);

          try (DirectoryStream<Path> stream = Files.newDirectoryStream(srcDir)) {
            for (Path f : stream) {
              if (!Files.isRegularFile(f)) {
                log.debug("skip non-regular file: {}", f);
                continue;
              }

              regularFiles++;

              String dstName = id + "__" + f.getFileName();
              Path dst = tmp.resolve(type).resolve(part).resolve(dstName);

              // dst가 남아있으면 삭제(심볼릭링크든 파일이든)
              if (Files.exists(dst) || Files.isSymbolicLink(dst)) {
                Files.delete(dst);
                log.debug("deleted existing: {}", dst);
              }

              try {
                // 심볼릭 링크 생성 (파일시스템이 달라도 작동)
                Files.createSymbolicLink(dst, f);
                symlinksMade++;
                log.debug("created symlink: {} => {}", dst, f);
              } catch (IOException e) {
                log.error("FAILED create symlink: {} => {}", dst, f, e);
                throw e;
              }
            }
          }
        }
      }
    }

    if (symlinksMade == 0) {
      throw new IOException(
          "No symlinks created. regularFiles="
              + regularFiles
              + ", scannedDirs="
              + scannedDirs
              + ", noDir="
              + noDir);
    }

    log.info("tmp dataset created: {}", tmp);
    log.info(
        "summary: scannedDirs={}, noDir={}, regularFiles={}, symlinksMade={}",
        scannedDirs,
        noDir,
        regularFiles,
        symlinksMade);

    return uid;
  }

  private static Path toPath(String p) {
    if (p.startsWith("~/")) {
      return Paths.get(System.getProperty("user.home")).resolve(p.substring(2)).normalize();
    }
    return Paths.get(p).toAbsolutePath().normalize();
  }
  // TODO 미사용 끝
}
