package com.ga.common.file;

import com.ga.common.exception.BizException;
import com.ga.common.exception.ErrorCode;
import com.ga.common.util.SecurityUtil;
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.time.LocalDate;
import java.util.List;
import java.util.UUID;

/**
 * 파일 업로드/다운로드. 로컬 파일시스템 저장 (S3 등으로 교체 가능한 구조).
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class FileService {

    private final FileMapper mapper;

    @Value("${ga.file.upload-dir:./uploads}")
    private String uploadDir;

    @Transactional
    public FileVO upload(MultipartFile file, String fileGroup) {
        if (file == null || file.isEmpty()) {
            throw new BizException(ErrorCode.FILE_UPLOAD_FAIL, "빈 파일입니다");
        }
        String original = file.getOriginalFilename();
        if (original == null) original = "unknown";
        String ext = extension(original);
        String stored = UUID.randomUUID() + (ext.isEmpty() ? "" : "." + ext);
        String relPath = LocalDate.now().toString().replace("-", "/");
        Path dir = Paths.get(uploadDir, relPath);
        try {
            Files.createDirectories(dir);
            Path dest = dir.resolve(stored);
            file.transferTo(dest.toFile());

            FileVO vo = new FileVO();
            vo.setFileGroup(fileGroup);
            vo.setOriginalName(original);
            vo.setStoredName(stored);
            vo.setFilePath(dest.toAbsolutePath().toString());
            vo.setFileSize(file.getSize());
            vo.setContentType(file.getContentType());
            vo.setExtension(ext);
            vo.setCreatedBy(SecurityUtil.getCurrentUserId());
            vo.setCreatedAt(java.time.LocalDateTime.now());
            mapper.insert(vo);
            return vo;
        } catch (IOException e) {
            log.error("File upload error", e);
            throw new BizException(ErrorCode.FILE_UPLOAD_FAIL, e.getMessage());
        }
    }

    /**
     * byte[] 콘텐츠를 파일로 저장하고 fileId를 반환한다.
     * MultipartFile 없이 프로그래밍 방식으로 파일을 생성할 때 사용.
     */
    @Transactional
    public Long saveBytes(byte[] content, String fileName, String fileGroup, String contentType) {
        String ext = extension(fileName);
        String stored = UUID.randomUUID() + (ext.isEmpty() ? "" : "." + ext);
        String relPath = LocalDate.now().toString().replace("-", "/");
        Path dir = Paths.get(uploadDir, relPath);
        try {
            Files.createDirectories(dir);
            Path dest = dir.resolve(stored);
            Files.write(dest, content);

            FileVO vo = new FileVO();
            vo.setFileGroup(fileGroup);
            vo.setOriginalName(fileName);
            vo.setStoredName(stored);
            vo.setFilePath(dest.toAbsolutePath().toString());
            vo.setFileSize((long) content.length);
            vo.setContentType(contentType);
            vo.setExtension(ext);
            vo.setCreatedBy(SecurityUtil.getCurrentUserId());
            vo.setCreatedAt(java.time.LocalDateTime.now());
            mapper.insert(vo);
            return vo.getFileId();
        } catch (IOException e) {
            log.error("File save error", e);
            throw new BizException(ErrorCode.FILE_UPLOAD_FAIL, e.getMessage());
        }
    }

    public FileVO download(Long fileId) {
        FileVO vo = mapper.selectById(fileId);
        if (vo == null) throw new BizException(ErrorCode.FILE_NOT_FOUND);
        mapper.incrementDownloadCount(fileId);
        return vo;
    }

    public List<FileVO> listByGroup(String fileGroup) {
        return mapper.selectByGroup(fileGroup);
    }

    @Transactional
    public void delete(Long fileId) {
        FileVO vo = mapper.selectById(fileId);
        if (vo == null) throw new BizException(ErrorCode.FILE_NOT_FOUND);
        mapper.markDeleted(fileId);
        // 물리 삭제는 배치로 처리
    }

    private String extension(String name) {
        int idx = name.lastIndexOf('.');
        return idx >= 0 ? name.substring(idx + 1).toLowerCase() : "";
    }
}
