package com.erp.service;

import com.erp.common.BaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class CompanyManagementService extends BaseService {

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

    // ──────────────────────────────────────────────────
    // 회사 삭제 (soft delete + 파일 정리)
    // ──────────────────────────────────────────────────

    @Transactional
    public Map<String, Object> deleteCompany(Map<String, Object> params) {
        String companyCode = (String) params.get("company_code");
        boolean createBackup = !Boolean.FALSE.equals(params.get("create_backup")); // 기본 true

        // 1. 회사 존재 확인
        Map<String, Object> existing = sqlSession.selectOne("companyManagement.selectCompanyByCode",
                Map.of("company_code", companyCode));
        if (existing == null) {
            throw new IllegalArgumentException("존재하지 않는 회사입니다.");
        }

        String companyName = (String) existing.get("company_name");

        // 2. 파일 정리
        try {
            cleanupCompanyFiles(companyCode, createBackup);
            log.info("회사 파일 정리 완료: companyCode={}, backup={}", companyCode, createBackup);
        } catch (IOException e) {
            log.error("회사 파일 정리 실패: companyCode={}", companyCode, e);
            throw new RuntimeException("회사 파일 정리 중 오류가 발생했습니다: " + e.getMessage(), e);
        }

        // 3. DB soft delete
        sqlSession.update("companyManagement.softDeleteCompany", Map.of("company_code", companyCode));
        log.info("회사 삭제 완료: companyCode={}, companyName={}", companyCode, companyName);

        Map<String, Object> data = new LinkedHashMap<>();
        data.put("company_code", companyCode);
        data.put("company_name", companyName);
        data.put("backup_created", createBackup);
        data.put("deleted_at", Instant.now().toString());
        return data;
    }

    // ──────────────────────────────────────────────────
    // 회사별 디스크 사용량
    // ──────────────────────────────────────────────────

    public Map<String, Object> getCompanyDiskUsage(String companyCode) {
        Path companyDir = Paths.get(uploadBasePath, companyCode);
        long[] stats = calculateDirStats(companyDir);

        Map<String, Object> data = new LinkedHashMap<>();
        data.put("company_code", companyCode);
        data.put("file_count", stats[0]);
        data.put("total_size", stats[1]);
        data.put("total_size_mb", toMB(stats[1]));
        data.put("last_checked", Instant.now().toString());
        return data;
    }

    // ──────────────────────────────────────────────────
    // 전체 회사 디스크 사용량
    // ──────────────────────────────────────────────────

    public Map<String, Object> getAllCompaniesDiskUsage() {
        List<Map<String, Object>> companies = new ArrayList<>();
        long totalFiles = 0;
        long totalSize = 0;

        // DB에서 활성 회사 코드 목록 조회
        List<Map<String, Object>> companyCodes = sqlSession.selectList(
                "companyManagement.selectAllCompanyCodes", new HashMap<>());

        for (Map<String, Object> row : companyCodes) {
            String code = (String) row.get("company_code");
            Path dir = Paths.get(uploadBasePath, code);
            long[] stats = calculateDirStats(dir);

            Map<String, Object> entry = new LinkedHashMap<>();
            entry.put("company_code", code);
            entry.put("file_count", stats[0]);
            entry.put("total_size", stats[1]);
            entry.put("total_size_mb", toMB(stats[1]));
            companies.add(entry);

            totalFiles += stats[0];
            totalSize += stats[1];
        }

        Map<String, Object> summary = new LinkedHashMap<>();
        summary.put("total_companies", companies.size());
        summary.put("total_files", totalFiles);
        summary.put("total_size", totalSize);
        summary.put("total_size_mb", toMB(totalSize));

        Map<String, Object> data = new LinkedHashMap<>();
        data.put("companies", companies);
        data.put("summary", summary);
        data.put("last_checked", Instant.now().toString());
        return data;
    }

    // ──────────────────────────────────────────────────
    // 내부 유틸
    // ──────────────────────────────────────────────────

    private void cleanupCompanyFiles(String companyCode, boolean createBackup) throws IOException {
        Path companyDir = Paths.get(uploadBasePath, companyCode);
        if (!Files.exists(companyDir)) {
            log.debug("회사 파일 디렉토리 없음 (건너뜀): {}", companyDir);
            return;
        }

        if (createBackup) {
            // 백업 디렉토리로 이동
            Path backupDir = Paths.get(uploadBasePath, ".backup",
                    companyCode + "_" + System.currentTimeMillis());
            Files.createDirectories(backupDir.getParent());
            Files.move(companyDir, backupDir, StandardCopyOption.REPLACE_EXISTING);
            log.info("회사 파일 백업 완료: {} → {}", companyDir, backupDir);
        } else {
            // 완전 삭제
            Files.walk(companyDir)
                    .sorted(Comparator.reverseOrder())
                    .forEach(path -> {
                        try {
                            Files.delete(path);
                        } catch (IOException e) {
                            log.warn("파일 삭제 실패: {}", path, e);
                        }
                    });
            log.info("회사 파일 삭제 완료: {}", companyDir);
        }
    }

    private long[] calculateDirStats(Path dir) {
        long[] stats = {0L, 0L}; // [fileCount, totalSize]
        if (!Files.exists(dir) || !Files.isDirectory(dir)) {
            return stats;
        }
        try {
            Files.walk(dir)
                    .filter(Files::isRegularFile)
                    .forEach(file -> {
                        stats[0]++;
                        try {
                            stats[1] += Files.size(file);
                        } catch (IOException e) {
                            log.warn("파일 크기 조회 실패: {}", file, e);
                        }
                    });
        } catch (IOException e) {
            log.warn("디렉토리 탐색 실패: {}", dir, e);
        }
        return stats;
    }

    private double toMB(long bytes) {
        return new BigDecimal(bytes)
                .divide(BigDecimal.valueOf(1024 * 1024), 2, RoundingMode.HALF_UP)
                .doubleValue();
    }
}
