package com.ga.api.service.attachment;

import com.ga.common.exception.BizException;
import com.ga.common.exception.ErrorCode;
import com.ga.common.model.PageResponse;
import com.ga.common.util.SecurityUtil;
import com.ga.core.mapper.attachment.AttachmentMapper;
import com.ga.core.vo.attachment.AttachmentResp;
import com.ga.core.vo.attachment.AttachmentSearchParam;
import com.ga.core.vo.attachment.AttachmentVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.UUID;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class AttachmentService {

    private final AttachmentMapper mapper;

    @Value("${app.attachment.path:./uploads}")
    private String uploadBasePath;

    public PageResponse<AttachmentResp> list(AttachmentSearchParam param) {
        param.startPage();
        return PageResponse.of(mapper.selectList(param));
    }

    public List<AttachmentResp> listByRef(String refType, Long refId) {
        return mapper.selectByRef(refType, refId);
    }

    public AttachmentResp detail(Long attachmentId) {
        AttachmentResp resp = mapper.selectById(attachmentId);
        if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
        return resp;
    }

    /**
     * 파일 업로드 + 메타 저장.
     * 파일 저장 실패 시 경고 로그 후 메타만 저장하는 fallback.
     */
    @Transactional
    public Long upload(MultipartFile file, String refType, Long refId) {
        String originalName = file.getOriginalFilename() != null ? file.getOriginalFilename() : "unknown";
        String storedPath = null;

        try {
            Path dir = Paths.get(uploadBasePath, refType.toLowerCase());
            Files.createDirectories(dir);
            String storedName = UUID.randomUUID() + "_" + originalName;
            Path dest = dir.resolve(storedName);
            file.transferTo(dest);
            storedPath = dest.toString();
        } catch (IOException e) {
            log.warn("파일 저장 실패 (fallback: 메타만 저장): refType={}, refId={}, file={}, error={}",
                    refType, refId, originalName, e.getMessage());
            storedPath = "[STORAGE_FAILED]/" + originalName;
        }

        AttachmentVO vo = new AttachmentVO();
        vo.setRefType(refType);
        vo.setRefId(refId);
        vo.setFileName(originalName);
        vo.setFilePath(storedPath);
        vo.setFileSize(file.getSize());
        vo.setMimeType(file.getContentType());
        vo.setUploadedBy(SecurityUtil.getCurrentUserId());
        mapper.insert(vo);
        return vo.getAttachmentId();
    }

    /**
     * 파일 다운로드를 위한 실제 경로 반환.
     */
    public Path resolveFilePath(Long attachmentId) {
        AttachmentResp resp = mapper.selectById(attachmentId);
        if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
        if (resp.getFilePath() == null || resp.getFilePath().startsWith("[STORAGE_FAILED]")) {
            throw new BizException(ErrorCode.NOT_FOUND, "파일이 저장되지 않아 다운로드할 수 없습니다");
        }
        return Paths.get(resp.getFilePath());
    }

    @Transactional
    public void delete(Long attachmentId) {
        AttachmentResp resp = mapper.selectById(attachmentId);
        if (resp == null) throw new BizException(ErrorCode.NOT_FOUND);
        // 실제 파일 삭제 시도 (실패해도 메타 삭제 진행)
        if (resp.getFilePath() != null && !resp.getFilePath().startsWith("[STORAGE_FAILED]")) {
            try {
                Files.deleteIfExists(Paths.get(resp.getFilePath()));
            } catch (IOException e) {
                log.warn("파일 삭제 실패: path={}", resp.getFilePath(), e);
            }
        }
        mapper.delete(attachmentId);
    }
}
