package com.zioinfo.mes.uiws.schedule.service;

import com.zioinfo.mes.uiws.common.UiwsApiException;
import com.zioinfo.mes.uiws.common.UiwsCurrentUser;
import com.zioinfo.mes.uiws.common.UiwsErrorCode;
import com.zioinfo.mes.uiws.schedule.dto.ScheduleDtos.AttachmentDto;
import com.zioinfo.mes.uiws.schedule.mapper.ScheduleMapper;
import com.zioinfo.mes.uiws.schedule.model.UiwsAttach;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;

/**
 * 첨부파일(폴리모픽 SCHEDULE/DIARY) 업로드·삭제·연결. 원본 com.urp.uiws.schedule.service.AttachmentService 이식.
 */
@Service
@RequiredArgsConstructor
public class AttachmentService {

    public static final String REF_SCHEDULE = "SCHEDULE";
    public static final String REF_DIARY = "DIARY";
    private static final Set<String> VALID_REF_TYPES = Set.of(REF_SCHEDULE, REF_DIARY);

    private final ScheduleMapper mapper;
    private final FileStorageService fileStorageService;

    @Transactional
    public AttachmentDto upload(String refType, Long refId, MultipartFile file) {
        String type = normalizeRefType(refType);
        if (refId == null) {
            throw new UiwsApiException(UiwsErrorCode.INVALID_REQUEST, "refId 는 필수입니다.");
        }
        FileStorageService.StoredFile stored = fileStorageService.store(file);

        String actor = UiwsCurrentUser.id();
        UiwsAttach a = new UiwsAttach();
        a.setRefType(type);
        a.setRefId(refId);
        a.setFileNm(stored.originalName());
        a.setFilePath(stored.relativePath());
        a.setFileSize(stored.size());
        a.setCreatedBy(actor);
        a.setCreatedAt(LocalDateTime.now());
        mapper.insertAttach(a);
        return AttachmentDto.from(a);
    }

    public record DownloadFile(String fileNm, Path path) {
    }

    @Transactional(readOnly = true)
    public DownloadFile download(Long attachId) {
        UiwsAttach a = mapper.findAttachById(attachId);
        if (a == null) {
            throw new UiwsApiException(UiwsErrorCode.ATTACH_NOT_FOUND);
        }
        Path path = fileStorageService.resolve(a.getFilePath());
        if (!Files.exists(path)) {
            throw new UiwsApiException(UiwsErrorCode.ATTACH_NOT_FOUND, "파일이 존재하지 않습니다.");
        }
        return new DownloadFile(a.getFileNm(), path);
    }

    @Transactional
    public void delete(Long attachId) {
        UiwsAttach a = mapper.findAttachById(attachId);
        if (a == null) {
            throw new UiwsApiException(UiwsErrorCode.ATTACH_NOT_FOUND);
        }
        fileStorageService.delete(a.getFilePath());
        mapper.deleteAttachById(attachId);
    }

    /** 일정/일지 저장 시 attachmentIds 의 첨부들을 해당 대상으로 귀속(확정). */
    @Transactional
    public void link(String refType, Long refId, List<Long> attachmentIds) {
        String type = normalizeRefType(refType);
        if (attachmentIds == null || attachmentIds.isEmpty()) {
            return;
        }
        String actor = UiwsCurrentUser.id();
        for (Long id : attachmentIds) {
            if (id == null) {
                continue;
            }
            mapper.updateAttachRef(id, type, refId, actor);
        }
    }

    @Transactional(readOnly = true)
    public List<AttachmentDto> list(String refType, Long refId) {
        return mapper.findAttachByRef(normalizeRefType(refType), refId)
                .stream().map(AttachmentDto::from).toList();
    }

    /** 대상 삭제 시 귀속 첨부 일괄 제거(물리파일 포함). */
    @Transactional
    public void deleteByRef(String refType, Long refId) {
        String type = normalizeRefType(refType);
        List<UiwsAttach> rows = mapper.findAttachByRef(type, refId);
        for (UiwsAttach a : rows) {
            fileStorageService.delete(a.getFilePath());
        }
        mapper.deleteAttachByRef(type, refId);
    }

    private String normalizeRefType(String refType) {
        String type = refType == null ? "" : refType.trim().toUpperCase();
        if (!VALID_REF_TYPES.contains(type)) {
            throw new UiwsApiException(UiwsErrorCode.ATTACH_REF_TYPE_INVALID);
        }
        return type;
    }
}
