package incheon.ags.dss.regen.service.impl;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.util.List;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import de.huxhorn.sulky.ulid.ULID;
import incheon.ags.dss.regen.mapper.UrbBcrnImgDtlMapper;
import incheon.ags.dss.regen.service.UrbBcrnImgDtlService;
import incheon.ags.dss.regen.vo.UrbBcrnImgDtlVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Service("urbBcrnImgService")
@RequiredArgsConstructor
@Slf4j
public class UrbBcrnImgDtlServiceImpl implements UrbBcrnImgDtlService {

    private final UrbBcrnImgDtlMapper urbBcrnImgDtlMapper;
    
    // --- 파일 업로드 표준 로직 (dss.bcrnImg.path 경로 사용) ---
    @Value("${Globals.dss.upload.path}") // (경로 수정)
    private String uploadPath;
    
    @Value("${Globals.dss.resource.path}") // (경로 수정)
    private String resourcePath;
    
    private Path absoluteUploadPath;
    private final ULID ulid = new ULID();

    @PostConstruct
    public void init() {
        // (배경 이미지는 용량이 클 수 있으므로 userMdl과 다른 경로 사용 가능)
        this.absoluteUploadPath = Paths.get(uploadPath).resolve("bcrnImg").toAbsolutePath().normalize();
        log.info("배경 이미지 업로드 절대 경로: {}", this.absoluteUploadPath);
        if (!Files.exists(this.absoluteUploadPath)) {
            try {
                Files.createDirectories(this.absoluteUploadPath);
            } catch (Exception e) {
            	// [보안 조치] 경로 정보 노출 방지를 위해 로그 메시지 분리
                log.error("배경 이미지 업로드 디렉토리 생성 실패 (경로 확인 필요)");
                log.debug("Directory Creation Error Details", e);
                
                // [로직 조치] 디렉토리가 없으면 진행 불가하므로 중단
                throw new RuntimeException("서버 스토리지 초기화 실패", e);
            }
        }
    }
    // --- 파일 업로드 표준 로직 ---

    @Override
    public List<UrbBcrnImgDtlVO> selectUrbBcrnImgDtlList(UrbBcrnImgDtlVO searchVO) throws Exception {
        return urbBcrnImgDtlMapper.selectUrbBcrnImgDtlList(searchVO);
    }

    @Override
    public int selectUrbBcrnImgDtlListCnt(UrbBcrnImgDtlVO searchVO) throws Exception {
        return urbBcrnImgDtlMapper.selectUrbBcrnImgDtlListCnt(searchVO);
    }
    
    @Override
    public UrbBcrnImgDtlVO selectUrbBcrnImgDtlDetail(UrbBcrnImgDtlVO vo) throws Exception {
        return urbBcrnImgDtlMapper.selectUrbBcrnImgDtlDetail(vo);
    }
    
    @Override
    @Transactional
    public int saveUrbBcrnImgDtl(UrbBcrnImgDtlVO vo, MultipartFile file) throws Exception {
        
        // --- 1. 파일 처리 ---
        if (file != null && !file.isEmpty()) {
            log.info("새 파일 업로드 감지: {}", file.getOriginalFilename());
            
            // 1-1. (수정 시) 기존 파일 삭제 로직
            if (vo.getImgNo() > 0) {
                UrbBcrnImgDtlVO oldData = urbBcrnImgDtlMapper.selectUrbBcrnImgDtlDetail(vo);
                if (oldData != null && oldData.getFlpth() != null) {
                    try {
                        Files.deleteIfExists(Paths.get(oldData.getFlpth()));
                        log.info("기존 파일 삭제 완료: {}", oldData.getFlpth());
                    } catch (Exception e) {
                    	log.warn("기존 파일 삭제 실패 (파일 경로 접근 불가 또는 권한 부족)");
                        log.debug("Delete Failed", e);
                    }
                }
            }

            // 1-2. 새 파일 저장 (표준 로직)
            String year = String.valueOf(LocalDate.now().getYear());
            String month = String.format("%02d", LocalDate.now().getMonthValue());
            Path absoluteDirPath = absoluteUploadPath.resolve(year).resolve(month);
            
            if (!Files.exists(absoluteDirPath)) {
                Files.createDirectories(absoluteDirPath);
            }
            
            String orgnlFileNm = file.getOriginalFilename();
            String extension = StringUtils.getFilenameExtension(orgnlFileNm);
            String strgFileNm = ulid.nextValue().toString() + (StringUtils.hasText(extension) ? "." + extension : "");
            
            Path filePath = absoluteDirPath.resolve(strgFileNm);
            file.transferTo(filePath.toFile()); // 파일 물리적 저장
            
            // 1-3. VO에 파일 메타데이터 세팅
            String relativePath = "bcrnImg/" + year + "/" + month + "/" + strgFileNm;
            vo.setTrgtUrl(resourcePath + "/" + relativePath);
            vo.setFlpth(filePath.toString());
            vo.setFileNm(orgnlFileNm);
            vo.setFilesiz(file.getSize());
        }
        
        // --- 2. DB 저장 ---
        if (vo.getImgNo() > 0) {
            log.info("Updating UrbBcrnImg (imgNo: {})", vo.getImgNo());
            urbBcrnImgDtlMapper.updateUrbBcrnImgDtl(vo);
            return vo.getImgNo();
        } else {
            log.info("Inserting UrbBcrnImg (fileNm: {})", vo.getFileNm());
            urbBcrnImgDtlMapper.insertUrbBcrnImgDtl(vo);
            return vo.getImgNo();
        }
    }
    
    @Override
    @Transactional
    public int deleteUrbBcrnImgDtl(UrbBcrnImgDtlVO vo) throws Exception {
        log.warn("Deleting UrbBcrnImg (imgNo: {})...", vo.getImgNo());
        
        // 1. DB에서 파일 정보를 먼저 조회
        UrbBcrnImgDtlVO fileInfo = urbBcrnImgDtlMapper.selectUrbBcrnImgDtlDetail(vo);
        
        if (fileInfo != null && fileInfo.getFlpth() != null) {
            // 2. 실제 물리적 파일 삭제
            try {
                Files.deleteIfExists(Paths.get(fileInfo.getFlpth()));
                log.info("물리적 파일 삭제 완료: {}", fileInfo.getFlpth());
            } catch (Exception e) {
            	log.warn("물리적 파일 삭제 처리를 완료하지 못했습니다.");
                log.debug("Physical File Delete Failed", e);
            }
        }
        
        // 3. (연관 삭제) 이 이미지를 사용하던 "배치" 정보(urb_mdl_mst)도 삭제
        // TODO: urbMdlMstMapper.deleteByImgNo(vo.getImgNo()) 호출 필요
        
        // 4. DB 레코드 삭제
        urbBcrnImgDtlMapper.deleteUrbBcrnImgDtl(vo);
        return 1;
    }
}