package ru.soune.nocopy.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import ru.soune.nocopy.dto.*;
import ru.soune.nocopy.dto.file.*;
import ru.soune.nocopy.entity.file.*;
import ru.soune.nocopy.entity.file.moderation.ModerationLog;
import ru.soune.nocopy.entity.file.permission.PermissionType;
import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity;
import ru.soune.nocopy.exception.*;
import ru.soune.nocopy.repository.*;
import ru.soune.nocopy.service.file.cloud.CloudStorageService;
import ru.soune.nocopy.service.file.moderation.ModerationService;
import ru.soune.nocopy.service.file.permission.FileAccessService;
import ru.soune.nocopy.service.register.AuthService;
import ru.soune.nocopy.service.file.FileEntityService;
import ru.soune.nocopy.service.file.FileStatsService;

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.*;
import java.util.stream.Collectors;

@Slf4j
@Component
@RequiredArgsConstructor
public class FileEntityHandler implements RequestHandler {

    private final FileEntityService fileEntityService;

    private final AuthService authService;

    private final FileStatsService fileStatsService;

    private final ObjectMapper objectMapper;

    private final FileEntityRepository fileEntityRepository;

    private final FileMonitoringRepository fileMonitoringRepository;

    private final CloudStorageService cloudStorageService;

    private final ModerationService moderationService;

    private final FileAppealRepository fileAppealRepository;

    private final ModerationLogRepository moderationLogRepository;

    private final ImageHashRepository imageHashRepository;

    private final FileAccessService fileAccessService;

    @Override
    public BaseResponse handle(BaseRequest request) {
        try {
            FileEntityRequest fileRequest = objectMapper.convertValue(
                    request.getMessageBody(), FileEntityRequest.class);

            String action = fileRequest.getAction();

            switch (action) {
                case "file_info":
                    return handleGetFileInfo(request, fileRequest);
                case "user_files_info":
                    return handleGetFilesUserInfo(request, fileRequest);
                case "file_by_session":
                    return handleGetFileBySession(request, fileRequest);
                case "user_files":
                    return handleGetUserFiles(request, fileRequest);
                case "search_files":
                    return handleSearchFiles(request, fileRequest);
                case "storage_usage":
                    return handleGetStorageUsage(request, fileRequest);
                case "check_protected":
                    return handleProtectFile(request, fileRequest);
                case "delete_file":
                    return handleDeleteFile(request, fileRequest);
                case "change_file_status":
                    return handleChangeStatus(request, fileRequest);
                case "delete_files":
                    return handleDeleteFiles(request, fileRequest);
                case "moderate_file":
                    return handleModerateFile(request, fileRequest);
                case "submit_appeal":
                    return handleSubmitAppeal(request, fileRequest);
                case "review_appeal":
                    return handleReviewAppeal(request, fileRequest);
                case "user_appeals":
                    return handleGetUserAppeals(request, fileRequest);
                case "files_for_moderation":
                    return handleGetFilesForModeration(request, fileRequest);
                case "change_permission":
                    return handleChangePermission(request, fileRequest);
                default:
                    ActionResponse response = ActionResponse.builder()
                            .action(action)
                            .availableActions(Arrays.asList(
                                    "file_info", "file_by_session", "user_files",
                                    "search_files", "storage_usage", "delete_file",
                                    "user_files_info"))
                            .build();

                    return new BaseResponse(request.getMsgId(),
                            MessageCode.INVALID_ACTION.getCode(),
                            "Invalid action: " + action,
                            response);
            }
        } catch (Exception e) {
            log.error("Error in FileEntityHandler", e);
            return new BaseResponse(request.getMsgId(),
                    MessageCode.FILE_UPLOAD_ERROR.getCode(),
                    "Handler error: " + e.getMessage(),
                    null);
        }
    }

    private BaseResponse handleGetFilesUserInfo(BaseRequest request, FileEntityRequest fileRequest) {
        Long userId = authService.useUserAuthToken(fileRequest.getToken());
        FileInfoUserResponse userFileStats = fileStatsService.getUserFileStats(userId);

        return new BaseResponse(request.getMsgId(),
                MessageCode.SUCCESS.getCode(),
                MessageCode.SUCCESS.getDescription(),
                userFileStats);
    }

    private BaseResponse handleGetFileInfo(BaseRequest request, FileEntityRequest fileRequest) {
        try {
            Long userId =  authService.useUserAuthToken(fileRequest.getToken());
            FileEntityResponse fileInfo = fileEntityService.getById(fileRequest.getFileId(), request.getVersion());

            if (!fileInfo.getUserId().equals(userId)) {
                return new BaseResponse(request.getMsgId(),
                        MessageCode.ACCESS_DENIED.getCode(),
                        "Access denied",
                        null);
            }

            return new BaseResponse(request.getMsgId(),
                    MessageCode.SUCCESS.getCode(),
                    MessageCode.SUCCESS.getDescription(),
                    fileInfo);

        } catch (NotFoundAuthToken e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_TOKEN.getCode(),
                    "Authentication required",
                    null);
        } catch (Exception e) {
            log.error("Error getting file info", e);
            return new BaseResponse(request.getMsgId(),
                    MessageCode.FILE_UPLOAD_ERROR.getCode(),
                    "Failed to get file info: " + e.getMessage(),
                    null);
        }
    }

    private BaseResponse handleChangePermission(BaseRequest request, FileEntityRequest fileRequest) {
        try {
            Long userId =  authService.useUserAuthToken(fileRequest.getToken());
            FileEntityResponse fileInfo = fileEntityService.getById(fileRequest.getFileId(), request.getVersion());

            if (!fileInfo.getUserId().equals(userId)) {
                return new BaseResponse(request.getMsgId(),
                        MessageCode.ACCESS_DENIED.getCode(),
                        "Access denied",
                        null);
            }

            Map<String, Boolean> permissions = fileRequest.getPermissions();

            if (!PermissionType.areAllValid(permissions)) {
                return new BaseResponse(request.getMsgId(),
                        MessageCode.PERMISSION_NOT_VALID.getCode(),
                        MessageCode.PERMISSION_NOT_VALID.getDescription(),
                        null);
            }

            for (Map.Entry<String, Boolean> entry: permissions.entrySet()) {
                try {
                    fileAccessService.changePermission(fileInfo.getId(), PermissionType.valueOf(entry.getKey()),
                            entry.getValue());
                } catch (FileEntityNotFoundException e) {
                    return new BaseResponse(request.getMsgId(),
                            MessageCode.FILE_NOT_FOUND.getCode(),
                            MessageCode.FILE_NOT_FOUND.getDescription(),
                            null);
                }
            }

            return new BaseResponse(request.getMsgId(),
                    MessageCode.SUCCESS.getCode(),
                    MessageCode.SUCCESS.getDescription(),
                    fileInfo);
        } catch (NotFoundAuthToken e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_TOKEN.getCode(),
                    "Authentication required",
                    null);
        } catch (Exception e) {
            log.error("Error getting file info", e);
            return new BaseResponse(request.getMsgId(),
                    MessageCode.FILE_UPLOAD_ERROR.getCode(),
                    "Failed to get file info: " + e.getMessage(),
                    null);
        }
    }

    private BaseResponse handleGetFileBySession(BaseRequest request, FileEntityRequest fileRequest) {
        try {
            Long userId = authService.useUserAuthToken(fileRequest.getToken());
            FileEntityResponse fileInfo = fileEntityService.getByUploadSessionId(fileRequest.getUploadSessionId(),
                    request.getVersion());

            if (!fileInfo.getUserId().equals(userId)) {
                return new BaseResponse(request.getMsgId(),
                        MessageCode.ACCESS_DENIED.getCode(),
                        "Access denied",
                        null);
            }

            return new BaseResponse(request.getMsgId(),
                    MessageCode.SUCCESS.getCode(),
                    MessageCode.SUCCESS.getDescription(),
                    fileInfo);

        } catch (NotFoundAuthToken e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_TOKEN.getCode(),
                    "Authentication required",
                    null);
        } catch (Exception e) {
            log.error("Error getting file by session", e);
            return new BaseResponse(request.getMsgId(),
                    MessageCode.FILE_UPLOAD_ERROR.getCode(),
                    "Failed to get file: " + e.getMessage(),
                    null);
        }
    }

    private BaseResponse handleGetUserFiles(BaseRequest request, FileEntityRequest fileRequest) {
        try {
            Long userId = authService.useUserAuthToken(fileRequest.getToken());
            int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1;
            int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20;

            FileResponse files = fileEntityService.getUserFiles(userId, page, pageSize, request.getVersion());

            return new BaseResponse(request.getMsgId(),
                    MessageCode.SUCCESS.getCode(),
                    MessageCode.SUCCESS.getDescription(),
                    files);

        } catch (NotFoundAuthToken e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_TOKEN.getCode(),
                    "Authentication required",
                    null);
        } catch (Exception e) {
            log.error("Error getting user files", e);
            return new BaseResponse(request.getMsgId(),
                    MessageCode.FILE_UPLOAD_ERROR.getCode(),
                    "Failed to get files: " + e.getMessage(),
                    null);
        }
    }

    private BaseResponse handleChangeStatus(BaseRequest request, FileEntityRequest fileRequest) {
        FileStatus newStatus = fileRequest.getFileStatus();
        String fileId = fileRequest.getFileId();

        try {
            fileEntityService.changeFileStatus(newStatus ,fileRequest.getFileId());
        } catch (FileEntityNotFoundException e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.FILE_NOT_FOUND.getCode(),
                    MessageCode.FILE_NOT_FOUND.getDescription(),
                    FileEntityResponse.builder().id(fileId).build());
        }

        return new BaseResponse(request.getMsgId(),
                MessageCode.SUCCESS.getCode(),
                MessageCode.SUCCESS.getDescription(),
                Map.of("file_status", newStatus, "file_id", fileId));
    }

//    private BaseResponse handleSearchFiles(BaseRequest request, FileEntityRequest fileRequest) {
//        try {
//            Long userId = authService.useUserAuthToken(fileRequest.getToken());
//            int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1;
//            int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20;
//            String fileRequestSortBy = fileRequest.getSortBy();
//            String sortBy = !fileRequestSortBy.equals("") ? fileRequest.getSortBy(): "createdAt";
//            sortBy = sortBy.equals("fileName") ? "originalFileName" : sortBy;
//
//            String sortOrder = fileRequest.getSortOrder();
//            Sort.Direction direction = (sortOrder != null && sortOrder.equalsIgnoreCase("desc"))
//                    ? Sort.Direction.DESC
//                    : Sort.Direction.ASC;
//            Sort sort = Sort.by(direction, sortBy);
//
//            Pageable pageable = PageRequest.of(page, pageSize, sort);
//            FileResponse userFiles = fileEntityService.getUserFiles(userId, pageable, request.getVersion());
//
//            FileListResponse response = FileListResponse.builder()
//                    .files(userFiles.getFiles())
//                    .totalCount(userFiles.getTotalCount())
//                    .totalSize(userFiles.getTotalSize())
//                    .formattedTotalSize(fileEntityService.formatFileSize(userFiles.getTotalSize()))
//                    .page(page)
//                    .pageSize(pageSize)
//                    .sortBy(sortBy)
//                    .sortOrder(sortOrder)
//                    .build();
//
//            return new BaseResponse(request.getMsgId(),
//                    MessageCode.SUCCESS.getCode(),
//                    MessageCode.SUCCESS.getDescription(),
//                    response);
//
//        } catch (NotFoundAuthToken e) {
//            return new BaseResponse(request.getMsgId(),
//                    MessageCode.INVALID_TOKEN.getCode(),
//                    "Authentication required", null);
//        } catch (Exception e) {
//            log.error("Error searching files", e);
//            return new BaseResponse(request.getMsgId(),
//                    MessageCode.FILE_UPLOAD_ERROR.getCode(),
//                    "Failed to search files: " + e.getMessage(), null);
//        }
//    }

    private BaseResponse handleSearchFiles(BaseRequest request, FileEntityRequest fileRequest) {
        try {
            Long userId = authService.useUserAuthToken(fileRequest.getToken());
            int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1;
            int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20;

            String sortBy = fileRequest.getSortBy() != null ? fileRequest.getSortBy() : "fileName";
            SortOrder sortOrder = SortOrder.fromString(fileRequest.getSortOrder());

            FileResponse allFiles = fileEntityService.getUserFiles(userId, 1, 1000, request.getVersion());

            String searchQuery = fileRequest.getQuery() != null ? fileRequest.getQuery().toLowerCase().trim() : "";
            String[] searchTerms = searchQuery.split("\\s+");
            String filterType = fileRequest.getType();
            String dateFilter = fileRequest.getDateFilter();

            List<FileEntityResponse> filteredFiles = allFiles.getFiles().stream()
                    .filter(f -> matchesSearch(f, searchTerms, searchQuery, filterType))
                    .filter(f -> matchesDateFilter(f, dateFilter))
                    .collect(Collectors.toList());

            Comparator<FileEntityResponse> comparator = getComparator(sortBy, sortOrder);
            filteredFiles.sort(comparator);

            int start = (page - 1) * pageSize;
            int end = Math.min(start + pageSize, filteredFiles.size());

            if (start >= filteredFiles.size()) {
                return createEmptyResponse(request, page, pageSize, sortBy, sortOrder.getValue());
            }

            List<FileEntityResponse> paginatedFiles = filteredFiles.subList(start, end);
            long totalSize = paginatedFiles.stream()
                    .mapToLong(FileEntityResponse::getFileSize)
                    .sum();

            FileListResponse response = FileListResponse.builder()
                    .files(paginatedFiles)
                    .totalCount(filteredFiles.size())
                    .totalSize(totalSize)
                    .formattedTotalSize(fileEntityService.formatFileSize(totalSize))
                    .page(page)
                    .pageSize(pageSize)
                    .sortBy(sortBy)
                    .sortOrder(sortOrder.getValue())
                    .build();

            return new BaseResponse(request.getMsgId(),
                    MessageCode.SUCCESS.getCode(),
                    MessageCode.SUCCESS.getDescription(),
                    response);

        } catch (NotFoundAuthToken e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_TOKEN.getCode(),
                    "Authentication required", null);
        } catch (Exception e) {
            log.error("Error searching files", e);
            return new BaseResponse(request.getMsgId(),
                    MessageCode.FILE_UPLOAD_ERROR.getCode(),
                    "Failed to search files: " + e.getMessage(), null);
        }
    }

    /**
     *
     *     private BaseResponse handleSearchFiles(BaseRequest request, FileEntityRequest fileRequest) {
     *         try {
     *             Long userId = authService.useUserAuthToken(fileRequest.getToken());
     *             int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1;
     *             int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20;
     *
     *             String sortBy = fileRequest.getSortBy() != null ? fileRequest.getSortBy() : "fileName";
     *             SortOrder sortOrder = SortOrder.fromString(fileRequest.getSortOrder());
     *
     *             FileResponse allFiles = fileEntityService.getUserFiles(userId, 1, 1000, request.getVersion());
     *
     *             String searchQuery = fileRequest.getQuery() != null ? fileRequest.getQuery().toLowerCase().trim() : "";
     *             String[] searchTerms = searchQuery.split("\\s+");
     *             String filterType = fileRequest.getType();
     *             String dateFilter = fileRequest.getDateFilter();
     *
     *             List<FileEntityResponse> filteredFiles = allFiles.getFiles().stream()
     *                     .filter(f -> matchesSearch(f, searchTerms, searchQuery, filterType))
     *                     .filter(f -> matchesDateFilter(f, dateFilter))
     *                     .collect(Collectors.toList());
     *
     *             Comparator<FileEntityResponse> comparator = getComparator(sortBy, sortOrder);
     *             filteredFiles.sort(comparator);
     *
     *             int start = (page - 1) * pageSize;
     *             int end = Math.min(start + pageSize, filteredFiles.size());
     *
     *             if (start >= filteredFiles.size()) {
     *                 return createEmptyResponse(request, page, pageSize, sortBy, sortOrder.getValue());
     *             }
     *
     *             List<FileEntityResponse> paginatedFiles = filteredFiles.subList(start, end);
     *             long totalSize = paginatedFiles.stream()
     *                     .mapToLong(FileEntityResponse::getFileSize)
     *                     .sum();
     *
     *             FileListResponse response = FileListResponse.builder()
     *                     .files(paginatedFiles)
     *                     .totalCount(filteredFiles.size())
     *                     .totalSize(totalSize)
     *                     .formattedTotalSize(fileEntityService.formatFileSize(totalSize))
     *                     .page(page)
     *                     .pageSize(pageSize)
     *                     .sortBy(sortBy)
     *                     .sortOrder(sortOrder.getValue())
     *                     .build();
     *
     *             return new BaseResponse(request.getMsgId(),
     *                     MessageCode.SUCCESS.getCode(),
     *                     MessageCode.SUCCESS.getDescription(),
     *                     response);
     *
     *         } catch (NotFoundAuthToken e) {
     *             return new BaseResponse(request.getMsgId(),
     *                     MessageCode.INVALID_TOKEN.getCode(),
     *                     "Authentication required", null);
     *         } catch (Exception e) {
     *             log.error("Error searching files", e);
     *             return new BaseResponse(request.getMsgId(),
     *                     MessageCode.FILE_UPLOAD_ERROR.getCode(),
     *                     "Failed to search files: " + e.getMessage(), null);
     *         }
     *     }
     */

    private boolean matchesDateFilter(FileEntityResponse file, String dateFilter) {
        if (dateFilter == null || dateFilter.isEmpty()) {
            return true;
        }

        LocalDate fileDate = file.getCreatedAt().toLocalDate();
        LocalDate now = LocalDate.now();

        switch (dateFilter.toLowerCase()) {
            case "today":
                return fileDate.equals(now);
            case "week":
                return fileDate.isAfter(now.minusWeeks(1)) && !fileDate.isAfter(now);
            case "month":
                return fileDate.isAfter(now.minusMonths(1)) && !fileDate.isAfter(now);
            default:
                return true;
        }
    }

    private boolean matchesSearch(FileEntityResponse file, String[] searchTerms, String fullQuery, String type) {
        String fileName = getSafeString(file.getFileName());
        String originalFileName = getSafeString(file.getOriginalFileName());
        String mimeType = getSafeString(file.getMimeType());
        String fileFormat = getSafeString(file.getFileFormat());
        String fullFileName = fileName + " " + originalFileName;

        boolean matchesType = true;
        if (type != null && !type.isEmpty()) {
            matchesType = matchesMimeType(mimeType, type);
        }

        if (!matchesType) {
            return false;
        }

        if (fullQuery != null && !fullQuery.isEmpty()) {
            if (fullFileName.toLowerCase().contains(fullQuery)) {
                return true;
            }

            for (String term : searchTerms) {
                if (term.length() < 2) continue;
                if (fullFileName.toLowerCase().contains(term)) {
                    return true;
                }
            }

            if (fileFormat.toLowerCase().contains(fullQuery)) {
                return true;
            }

            if (mimeType.toLowerCase().contains(fullQuery)) {
                return true;
            }

            if (!fileFormat.isEmpty() && !fileName.toLowerCase().endsWith("." + fileFormat.toLowerCase())) {
                String fileNameWithExtension = fileName + "." + fileFormat;
                if (fileNameWithExtension.toLowerCase().contains(fullQuery)) {
                    return true;
                }
            }

            return false;
        }

        return true;
    }

    private boolean matchesMimeType(String mimeType, String filterType) {
        if (filterType == null || filterType.isEmpty()) return true;

        String mimeTypeLower = mimeType.toLowerCase();
        String filterTypeLower = filterType.toLowerCase().trim();

        return mimeTypeLower.equals(filterTypeLower);
    }

    private String getSafeString(String value) {
        return value != null ? value : "";
    }

    private Comparator<FileEntityResponse> getComparator(String sortBy, SortOrder sortOrder) {
        Comparator<FileEntityResponse> comparator;

        switch (sortBy) {
            case "fileSize":
                comparator = Comparator.comparingLong(
                        FileEntityResponse::getFileSize
                );
                break;

            case "fileType":
                comparator = Comparator.comparing(
                        FileEntityResponse::getMimeType);
                break;

            case "createdAt":
                comparator = Comparator.comparing(
                        FileEntityResponse::getCreatedAt,
                        Comparator.nullsLast(Comparator.naturalOrder())
                );
                break;

            case "updatedAt":
                comparator = Comparator.comparing(
                        FileEntityResponse::getUpdatedAt,
                        Comparator.nullsLast(Comparator.naturalOrder())
                );
                break;

            case "fileName":
                comparator = Comparator.comparing(
                        f -> getSafeString(f.getFileName()).toLowerCase(),
                        Comparator.nullsLast(Comparator.naturalOrder())
                );
                break;

            case "protectStatus":
                comparator = Comparator.comparing(
                        f -> getSafeString(f.getProtectStatus()).toLowerCase(),
                        Comparator.nullsLast(Comparator.naturalOrder())
                );
                break;

            case "supportId":
                comparator = Comparator.comparing(
                        FileEntityResponse::getSupportId,
                        Comparator.nullsLast(Comparator.naturalOrder())
                );
                break;
            case "monitoring":
                Map<String, Integer> monitoringOrder = Map.of(
                        "MONITORING_DAILY", 1,
                        "MONITORING_WEEKLY", 2,
                        "MONITORING_MONTHLY", 3
                );

                comparator = Comparator.comparing(
                        response -> monitoringOrder.getOrDefault(response.getMonitoring(),
                                Integer.MAX_VALUE), Comparator.nullsLast(Comparator.naturalOrder())
                );
                break;

            default:
                comparator = Comparator.comparing(
                        f -> getSafeString(f.getFileName()).toLowerCase(),
                        Comparator.nullsLast(Comparator.naturalOrder())
                );
                break;
        }

        return sortOrder == SortOrder.DESC ? comparator.reversed() : comparator;
    }

    private BaseResponse createEmptyResponse(BaseRequest request, int page, int pageSize,
                                             String sortBy, String sortOrder) {
        FileListResponse emptyResponse = FileListResponse.builder()
                .files(Collections.emptyList())
                .totalCount(0)
                .totalSize(0L)
                .formattedTotalSize("0 B")
                .page(page)
                .pageSize(pageSize)
                .sortBy(sortBy)
                .sortOrder(sortOrder)
                .build();

        return new BaseResponse(request.getMsgId(),
                MessageCode.SUCCESS.getCode(),
                MessageCode.SUCCESS.getDescription(),
                emptyResponse);
    }

    private BaseResponse handleGetUserFilesWithSort(BaseRequest request, FileEntityRequest fileRequest,
                                                    String sortBy, SortOrder sortOrder) {
        try {
            Long userId = authService.useUserAuthToken(fileRequest.getToken());
            int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1;
            int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20;

            FileResponse allFiles = fileEntityService.getUserFiles(userId, 1, 1000, request.getVersion());

            List<FileEntityResponse> files = new ArrayList<>(allFiles.getFiles());

            Comparator<FileEntityResponse> comparator = getComparator(sortBy, sortOrder);
            files.sort(comparator);

            int start = (page - 1) * pageSize;
            int end = Math.min(start + pageSize, files.size());

            if (start >= files.size()) {
                return createEmptyResponse(request, page, pageSize, sortBy, sortOrder.getValue());
            }

            List<FileEntityResponse> paginatedFiles = files.subList(start, end);
            long totalSize = paginatedFiles.stream()
                    .mapToLong(FileEntityResponse::getFileSize)
                    .sum();

            FileListResponse response = FileListResponse.builder()
                    .files(paginatedFiles)
                    .totalCount(files.size())
                    .totalSize(totalSize)
                    .formattedTotalSize(fileEntityService.formatFileSize(totalSize))
                    .page(page)
                    .pageSize(pageSize)
                    .sortBy(sortBy)
                    .sortOrder(sortOrder.getValue())
                    .build();

            return new BaseResponse(request.getMsgId(),
                    MessageCode.SUCCESS.getCode(),
                    MessageCode.SUCCESS.getDescription(),
                    response);

        } catch (NotFoundAuthToken e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_TOKEN.getCode(),
                    "Authentication required", null);
        } catch (Exception e) {
            log.error("Error getting user files with sort", e);
            return new BaseResponse(request.getMsgId(),
                    MessageCode.FILE_UPLOAD_ERROR.getCode(),
                    "Failed to get files: " + e.getMessage(),
                    null);
        }
    }

    private BaseResponse handleGetStorageUsage(BaseRequest request, FileEntityRequest fileRequest) {
        try {
            Long userId = authService.useUserAuthToken(fileRequest.getToken());
            long usage = fileEntityService.getUserStorageUsed(userId);

            StorageUsageResponse response = StorageUsageResponse.builder()
                    .userId(userId)
                    .storageUsed(usage)
                    .formattedStorageUsed(formatFileSize(usage))
                    .build();

            return new BaseResponse(request.getMsgId(),
                    MessageCode.SUCCESS.getCode(),
                    MessageCode.SUCCESS.getDescription(),
                    response);

        } catch (NotFoundAuthToken e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_TOKEN.getCode(),
                    "Authentication required",
                    null);
        } catch (Exception e) {
            log.error("Error getting storage usage", e);
            return new BaseResponse(request.getMsgId(),
                    MessageCode.FILE_UPLOAD_ERROR.getCode(),
                    "Failed to get storage usage: " + e.getMessage(),
                    null);
        }
    }

    private BaseResponse handleProtectFile(BaseRequest request, FileEntityRequest fileRequest) {
        String fileId = fileRequest.getFileId();
        Optional<FileEntity> optionalFileEntity = fileEntityRepository.findById(fileId);

        if (optionalFileEntity.isEmpty()) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.FILE_NOT_FOUND.getCode(),
                    "File not found exception: " + fileRequest.getFileId(),
                    null);
        }

        FileEntity fileEntity = optionalFileEntity.get();
        ProtectionStatus protectionStatus = fileEntity.getProtectionStatus();

        Integer code = protectionStatus == ProtectionStatus.PROTECTED ? MessageCode.FILE_IS_PROTECTED.getCode():
                MessageCode.FILE_IS_NOT_PROTECTED.getCode();
        String message = protectionStatus == ProtectionStatus.PROTECTED ? MessageCode.FILE_IS_PROTECTED.getDescription():
                MessageCode.FILE_IS_NOT_PROTECTED.getDescription();

        return new BaseResponse(request.getMsgId(), code, message, fileEntityService.getById(fileId,
                request.getVersion()));
    }

    @Transactional
    public BaseResponse handleDeleteFiles(BaseRequest request, FileEntityRequest fileRequest) {
        List<String> fileIds = fileRequest.getFileIds();
        List<String> deletedIds = new ArrayList<>();
        List<String> failedIds = new ArrayList<>();

        for (String fileId : fileIds) {
            try {
                FileEntity fileEntity = fileEntityRepository.findById(fileId)
                        .orElseThrow(() -> new FileEntityNotFoundException(fileId));

                deleteFileIfExists(fileEntity.getFilePath());
                deleteFileIfExists(fileEntity.getProtectedFilePath());
                deleteFileIfExists(fileEntity.getThumbnailPath());
                deleteFileIfExists(fileEntity.getMediumPath());

                deletedIds.add(fileId);
                log.info("Physical files deleted for entity: {}", fileId);

            } catch (Exception e) {
                failedIds.add(fileId);
                log.error("Failed to delete files for entity: {}", fileId, e);
            }
        }

        if (!deletedIds.isEmpty()) {
            fileMonitoringRepository.deleteAllByFileIdIn(deletedIds);
            moderationLogRepository.deleteAllByFileIdIn(deletedIds);
            imageHashRepository.deleteAllByFileIdIn(deletedIds);
            fileAppealRepository.deleteAllByFileIdIn(deletedIds);
            fileEntityRepository.deleteAllByIdIn(deletedIds);

            log.info("Deleted {} entities from database", deletedIds.size());
        }

        return new BaseResponse(
                request.getMsgId(),
                MessageCode.SUCCESS.getCode(),
                MessageCode.SUCCESS.getDescription(),
                failedIds
        );
    }

    private void deleteFileIfExists(String filePath) {
        if (filePath != null && !filePath.isEmpty()) {
            try {
                Path path = Paths.get(filePath);
                boolean deleted = Files.deleteIfExists(path);
                if (deleted) {
                    log.debug("Deleted file: {}", filePath);
                }
            } catch (IOException e) {
                log.warn("Failed to delete file: {}", filePath, e);
                throw new RuntimeException("Failed to delete file: " + filePath, e);
            }
        }
    }

    private BaseResponse handleDeleteFile(BaseRequest request, FileEntityRequest fileRequest) {
        try {
            Long userId = authService.useUserAuthToken(fileRequest.getToken());
            String fileId = fileRequest.getFileId();
            FileEntityResponse fileInfo = fileEntityService.getById(fileId, request.getVersion());
            DeleteFileResponse response;

            if (!fileInfo.getUserId().equals(userId)) {
                return new BaseResponse(request.getMsgId(),
                        MessageCode.ACCESS_DENIED.getCode(),
                        "Access denied",
                        null);
            }

            FileEntity fileEntity = fileEntityRepository.findById(fileId)
                    .orElseThrow(() -> new FileEntityNotFoundException(fileId));
            int fullDelete = fileRequest.getFullDelete() == null ? 0 : fileRequest.getFullDelete();

            if (fileEntity.getStatus().equals(FileStatus.DELETED) || fullDelete == 1) {
                cloudStorageService.deleteFromStorageDisk(fileEntity);
                response = DeleteFileResponse.builder()
                        .fileId(fileRequest.getFileId())
                        .message("File deleted from disk")
                        .build();
            } else {
                fileEntityService.softDeleteFileWithHash(fileEntity);

                response = DeleteFileResponse.builder()
                        .fileId(fileRequest.getFileId())
                        .message("File marked as deleted")
                        .build();
            }

            return new BaseResponse(request.getMsgId(),
                    MessageCode.SUCCESS.getCode(),
                    "File marked as deleted",
                    response);
        } catch (NotFoundAuthToken e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_TOKEN.getCode(),
                    "Authentication required",
                    null);
        } catch (Exception e) {
            log.error("Error deleting file", e);
            return new BaseResponse(request.getMsgId(),
                    MessageCode.FILE_UPLOAD_ERROR.getCode(),
                    "Failed to delete file: " + e.getMessage(),
                    null);
        }
    }

    private BaseResponse handleModerateFile(BaseRequest request, FileEntityRequest fileRequest) {
        try {
            //TODO добавить проверку на админа
//            Long moderatorId = authService.useAdminAuthToken(fileRequest.getToken());

            ModerationActionRequest moderationRequest = ModerationActionRequest.builder()
                    .fileId(fileRequest.getFileId())
                    .newStatus(fileRequest.getFileStatus())
                    .comment(fileRequest.getComment())
                    .build();

            //TODO set moderator ID
            moderationService.moderateFile(moderationRequest, 0L);

            return new BaseResponse(request.getMsgId(),
                    MessageCode.SUCCESS_MODERATION.getCode(),
                    MessageCode.SUCCESS_MODERATION.getDescription(),
                    FileEntityResponse.builder().id(fileRequest.getFileId()).build());

        } catch (SecurityException e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.ACCESS_DENIED.getCode(),
                    MessageCode.ACCESS_DENIED.getDescription(),
                    FileEntityResponse.builder().id(fileRequest.getFileId()).build());
        } catch (FileEntityNotFoundException e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.FILE_NOT_FOUND.getCode(),
                    MessageCode.FILE_NOT_FOUND.getDescription(),
                    FileEntityResponse.builder()
                            .id(fileRequest.getFileId()).build());
        } catch (IllegalStateException e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.ILLEGAL_STATE.getCode(),
                    MessageCode.ILLEGAL_STATE.getDescription(),
                    FileEntityResponse.builder()
                            .status(fileRequest.getFileStatus()).build());
        }
    }

    private BaseResponse handleSubmitAppeal(BaseRequest request, FileEntityRequest fileRequest) {
        try {
            Long userId = authService.useUserAuthToken(fileRequest.getToken());

            AppealRequest appealRequest = AppealRequest.builder()
                    .fileId(fileRequest.getFileId())
                    .appealReason(fileRequest.getAppealReason())
                    .additionalInfo(fileRequest.getAdditionalInfo())
                    .build();

            AppealResponse response = moderationService.submitAppeal(appealRequest, userId);

            return new BaseResponse(request.getMsgId(),
                    MessageCode.SUCCESS.getCode(),
                    "Appeal submitted successfully",
                    response);
        } catch (InvalidAppealException e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_FIELD.getCode(),
                    e.getMessage(),
                    null);
        } catch (Exception e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_FIELD.getCode(),
                    "Failed to submit appeal: " + e.getMessage(),
                    null);
        }
    }

    private BaseResponse handleReviewAppeal(BaseRequest request, FileEntityRequest fileRequest) {
        try {
            // TODO
//            Long moderatorId = authService.useAdminAuthToken(fileRequest.getToken());

            String appealId = fileRequest.getAppealId();
            Boolean approve = fileRequest.getApprove();
            String comment = fileRequest.getComment();

            if (appealId == null || approve == null) {
                return new BaseResponse(request.getMsgId(),
                        MessageCode.INVALID_FIELD.getCode(),
                        "appealId and approve are required",
                        null);
            }
            //TODO
            moderationService.reviewAppeal(appealId, approve, comment, 0L);

            return new BaseResponse(request.getMsgId(),
                    MessageCode.SUCCESS.getCode(),
                    "Appeal reviewed successfully",
                    Map.of(
                            "appealId", appealId,
                            "approved", approve,
                            "comment", comment
                    ));

        } catch (SecurityException e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.ACCESS_DENIED.getCode(),
                    "Admin access required",
                    null);
        } catch (IllegalStateException e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_FIELD.getCode(),
                    e.getMessage(),
                    null);
        } catch (Exception e) {
            log.error("Error reviewing appeal", e);
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_FIELD.getCode(),
                    "Failed to review appeal: " + e.getMessage(),
                    null);
        }
    }

    private BaseResponse handleGetUserAppeals(BaseRequest request, FileEntityRequest fileRequest) {
        try {
            Long userId = authService.useUserAuthToken(fileRequest.getToken());

            int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1;
            int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20;

            Page<AppealResponse> appeals = moderationService.getUserAppeals(userId, page - 1, pageSize);

            Map<String, Object> response = Map.of(
                    "appeals", appeals.getContent(),
                    "totalCount", appeals.getTotalElements(),
                    "totalPages", appeals.getTotalPages(),
                    "currentPage", page,
                    "pageSize", pageSize);

            return new BaseResponse(request.getMsgId(),
                    MessageCode.SUCCESS.getCode(),
                    MessageCode.SUCCESS.getDescription(),
                    response);

        } catch (SecurityException e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_TOKEN.getCode(),
                    "Authentication required",
                    null);
        } catch (Exception e) {
            log.error("Error getting user appeals", e);
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_FIELD.getCode(),
                    "Failed to get appeals: " + e.getMessage(),
                    null);
        }
    }

    private BaseResponse handleGetFilesForModeration(BaseRequest request, FileEntityRequest fileRequest) {
        try {
            //TODO
//            authService.useAdminAuthToken(fileRequest.getToken());

            int page = fileRequest.getPage() != null ? fileRequest.getPage() : 1;
            int pageSize = fileRequest.getPageSize() != null ? fileRequest.getPageSize() : 20;

            Page<FileEntity> files = moderationService.getFilesForModeration(page - 1, pageSize);

            List<Map<String, Object>> filesWithInfo = files.getContent().stream()
                    .map(file -> {
                        FileModerationInfo info = moderationService.getFileModerationInfo(file.getId());

                        Map<String, Object> fileMap = new HashMap<>();
                        fileMap.put("fileId", file.getId());
                        fileMap.put("fileName", file.getOriginalFileName());
                        fileMap.put("userId", file.getUserId());
                        fileMap.put("status", file.getStatus() != null ? file.getStatus().name() : null);

                        Map<String, Object> moderationInfoMap = new HashMap<>();
                        moderationInfoMap.put("hasActiveAppeal", info != null && info.getHasActiveAppeal());

                        if (info != null && info.getActiveAppeal() != null) {
                            Map<String, Object> appealInfoMap = new HashMap<>();
                            appealInfoMap.put("appealId", info.getActiveAppeal().getAppealId());
                            appealInfoMap.put("status", info.getActiveAppeal().getStatus());
                            appealInfoMap.put("createdAt", info.getActiveAppeal().getCreatedAt());
                            moderationInfoMap.put("appealInfo", appealInfoMap);
                        } else {
                            moderationInfoMap.put("appealInfo", null);
                        }

                        fileMap.put("moderationInfo", moderationInfoMap);

                        return fileMap;
                    })
                    .collect(Collectors.toList());

            Map<String, Object> response = Map.of(
                    "files", filesWithInfo,
                    "totalCount", files.getTotalElements(),
                    "totalPages", files.getTotalPages(),
                    "currentPage", page,
                    "pageSize", pageSize
            );

            return new BaseResponse(request.getMsgId(),
                    MessageCode.SUCCESS.getCode(),
                    MessageCode.SUCCESS.getDescription(),
                    response);

        } catch (SecurityException e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.ACCESS_DENIED.getCode(),
                    "Admin access required",
                    null);
        } catch (Exception e) {
            log.error("Error getting files for moderation", e);
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_FIELD.getCode(),
                    "Failed to get files: " + e.getMessage(),
                    null);
        }
    }

    private BaseResponse handleGetFileModerationInfo(BaseRequest request, FileEntityRequest fileRequest) {
        try {
            // TODO
            // authService.useAdminAuthToken(fileRequest.getToken());

            String fileId = fileRequest.getFileId();
            if (fileId == null) {
                return new BaseResponse(request.getMsgId(),
                        MessageCode.INVALID_FIELD.getCode(),
                        "fileId is required",
                        null);
            }

            FileModerationInfo moderationInfo = moderationService.getFileModerationInfo(fileId);

            FileEntity file = fileEntityRepository.findById(fileId)
                    .orElseThrow(() -> new FileEntityNotFoundException(fileId));

            Map<String, Object> fileInfoMap = new HashMap<>();
            fileInfoMap.put("id", file.getId());
            fileInfoMap.put("fileName", file.getOriginalFileName());
            fileInfoMap.put("storedFileName", file.getStoredFileName());
            fileInfoMap.put("fileSize", file.getFileSize());
            fileInfoMap.put("formattedSize", fileEntityService.formatFileSize(file.getFileSize()));
            fileInfoMap.put("mimeType", file.getMimeType());
            fileInfoMap.put("fileExtension", file.getFileExtension());
            fileInfoMap.put("checksum", file.getChecksum());
            fileInfoMap.put("status", file.getStatus() != null ? file.getStatus().name() : null);
            fileInfoMap.put("createdAt", file.getCreatedAt());
            fileInfoMap.put("updatedAt", file.getUpdatedAt());

            Map<String, Object> response = new HashMap<>();
            response.put("fileInfo", fileInfoMap);
            response.put("moderationInfo", moderationInfo != null ? moderationInfo : new HashMap<>());
            response.put("moderationHistory", getModerationHistory(fileId));
            response.put("appeals", getFileAppeals(fileId));

            return new BaseResponse(request.getMsgId(),
                    MessageCode.SUCCESS.getCode(),
                    MessageCode.SUCCESS.getDescription(),
                    response);

        } catch (SecurityException e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.ACCESS_DENIED.getCode(),
                    "Admin access required",
                    null);
        } catch (FileEntityNotFoundException e) {
            return new BaseResponse(request.getMsgId(),
                    MessageCode.FILE_NOT_FOUND.getCode(),
                    e.getMessage(),
                    null);
        } catch (Exception e) {
            log.error("Error getting file moderation info", e);
            return new BaseResponse(request.getMsgId(),
                    MessageCode.INVALID_FIELD.getCode(),
                    "Failed to get file info: " + e.getMessage(),
                    null);
        }
    }

    private List<Map<String, Object>> getModerationHistory(String fileId) {
        List<ModerationLog> logs = moderationLogRepository.findByFileIdOrderByCreatedAtDesc(fileId);
        if (logs == null || logs.isEmpty()) {
            return Collections.emptyList();
        }

        return logs.stream()
                .map(log -> {
                    Map<String, Object> logMap = new HashMap<>();
                    logMap.put("id", log.getId());
                    logMap.put("moderatorId", log.getModeratorId());
                    logMap.put("oldStatus", log.getOldStatus() != null ? log.getOldStatus().name() : null);
                    logMap.put("newStatus", log.getNewStatus() != null ? log.getNewStatus().name() : null);
                    logMap.put("reason", log.getReason());
                    logMap.put("comment", log.getComment());
                    logMap.put("createdAt", log.getCreatedAt());
                    return logMap;
                })
                .collect(Collectors.toList());
    }

    private List<Map<String, Object>> getFileAppeals(String fileId) {
        List<FileAppeal> appeals = fileAppealRepository.findByFileId(fileId);
        if (appeals == null || appeals.isEmpty()) {
            return Collections.emptyList();
        }

        return appeals.stream()
                .map(appeal -> {
                    Map<String, Object> appealMap = new HashMap<>();
                    appealMap.put("id", appeal.getId());
                    appealMap.put("userId", appeal.getUserId());
                    appealMap.put("appealReason", appeal.getAppealReason());
                    appealMap.put("additionalInfo", appeal.getAdditionalInfo());
                    appealMap.put("status", appeal.getStatus() != null ? appeal.getStatus().name() : null);
                    appealMap.put("adminComment", appeal.getAdminComment());
                    appealMap.put("moderatorId", appeal.getModeratorId());
                    appealMap.put("createdAt", appeal.getCreatedAt());
                    appealMap.put("resolvedAt", appeal.getResolvedAt());
                    return appealMap;
                })
                .collect(Collectors.toList());
    }

    private String formatFileSize(long size) {
        if (size < 1024) return size + " B";
        int exp = (int) (Math.log(size) / Math.log(1024));
        String pre = "KMGTPE".charAt(exp-1) + "";
        return String.format("%.1f %sB", size / Math.pow(1024, exp), pre);
    }
}
