package com.erp.service;

import com.erp.common.BaseService;
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.File;
import java.io.IOException;
import java.nio.file.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Pattern;

@Service
@RequiredArgsConstructor
@Slf4j
public class FileService extends BaseService {

    private final CommonService commonService;

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

    private static final String NS = "file.";
    private static final Pattern UNSAFE_CHARS = Pattern.compile("[/\\\\:*?\"<>|]");
    private static final long MAX_FILE_SIZE = 50L * 1024 * 1024; // 50MB

    // ── 목록 조회 ────────────────────────────────────────────────────────────

    public Map<String, Object> getFileList(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        commonService.applyPagination(params);
        Integer totalObj = sqlSession.selectOne(NS + "getFileListCnt", params);
        int totalCount = totalObj != null ? totalObj : 0;
        List<Map<String, Object>> list = sqlSession.selectList(NS + "getFileList", params);
        return commonService.buildListResponse(list, totalCount, params);
    }

    // ── 단건 조회 ─────────────────────────────────────────────────────────────

    public Map<String, Object> getFileInfo(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        return sqlSession.selectOne(NS + "getFileInfo", params);
    }

    public Map<String, Object> getFileInfoPublic(Map<String, Object> params) {
        // public 접근 — company_code 필터 없음
        return sqlSession.selectOne(NS + "getFileInfo", params);
    }

    // ── 타겟별 파일 목록 ────────────────────────────────────────────────────

    public List<Map<String, Object>> getFilesByTarget(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        return sqlSession.selectList(NS + "getFilesByTarget", params);
    }

    // ── 업로드 ────────────────────────────────────────────────────────────────

    @Transactional
    public List<Map<String, Object>> uploadFiles(List<MultipartFile> files,
                                                  Map<String, Object> params) throws IOException {
        String companyCode = (String) params.get("company_code");
        String writer = (String) params.getOrDefault("user_id", "system");
        String targetObjid = (String) params.getOrDefault("target_objid", null);
        String docType = (String) params.getOrDefault("doc_type", "DOCUMENT");
        String docTypeName = (String) params.getOrDefault("doc_type_name", "일반 문서");

        LocalDate today = LocalDate.now();
        String datePath = today.format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
        String companyDir = (companyCode != null && !companyCode.equals("*")) ? companyCode : "shared";
        Path saveDir = Paths.get(uploadDir, companyDir, datePath);
        Files.createDirectories(saveDir);

        List<Map<String, Object>> results = new ArrayList<>();

        for (MultipartFile file : files) {
            if (file.isEmpty()) continue;
            if (file.getSize() > MAX_FILE_SIZE) {
                throw new IllegalArgumentException("파일 크기가 50MB를 초과합니다: " + file.getOriginalFilename());
            }

            String originalName = file.getOriginalFilename();
            if (originalName == null) originalName = "unknown";
            String safeName = UNSAFE_CHARS.matcher(originalName).replaceAll("_")
                                         .replace(" ", "_");
            String timestamp = String.valueOf(System.currentTimeMillis());
            String savedFileName = timestamp + "_" + safeName;

            String ext = "";
            int dotIdx = originalName.lastIndexOf('.');
            if (dotIdx >= 0) ext = originalName.substring(dotIdx + 1).toLowerCase();

            Path destPath = saveDir.resolve(savedFileName);
            Files.copy(file.getInputStream(), destPath, StandardCopyOption.REPLACE_EXISTING);

            String filePath = "/" + companyDir + "/" + datePath + "/" + savedFileName;

            long objid = System.currentTimeMillis() + (long)(Math.random() * 1000);

            Map<String, Object> fileParams = new HashMap<>();
            fileParams.put("objid", objid);
            fileParams.put("target_objid", targetObjid);
            fileParams.put("saved_file_name", savedFileName);
            fileParams.put("real_file_name", originalName);
            fileParams.put("doc_type", docType);
            fileParams.put("doc_type_name", docTypeName);
            fileParams.put("file_size", file.getSize());
            fileParams.put("file_ext", ext);
            fileParams.put("file_path", filePath);
            fileParams.put("company_code", companyCode);
            fileParams.put("writer", writer);
            fileParams.put("status", "ACTIVE");
            fileParams.put("is_representative", false);

            sqlSession.insert(NS + "insertFile", fileParams);

            Map<String, Object> result = new HashMap<>();
            result.put("objid", objid);
            result.put("real_file_name", originalName);
            result.put("saved_file_name", savedFileName);
            result.put("file_size", file.getSize());
            result.put("file_ext", ext);
            result.put("file_path", filePath);
            result.put("doc_type", docType);
            results.add(result);
        }

        return results;
    }

    // ── 다운로드용 파일 경로 조회 ─────────────────────────────────────────────

    public Path resolveFilePath(Map<String, Object> fileInfo) {
        String filePath = (String) fileInfo.get("file_path");
        if (filePath == null) throw new IllegalStateException("파일 경로 정보가 없습니다.");
        // filePath: /companyDir/YYYY/MM/DD/savedFileName
        if (filePath.startsWith("/")) filePath = filePath.substring(1);
        return Paths.get(uploadDir, filePath);
    }

    // ── 소프트 삭제 ───────────────────────────────────────────────────────────

    @Transactional
    public void deleteFile(Map<String, Object> params) {
        commonService.applyCompanyCodeFilter(params);
        sqlSession.update(NS + "softDeleteFile", params);
    }
}
