package ru.soune.nocopy.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import ru.soune.nocopy.dto.statistic.UserFilesStatisticResponse;
import ru.soune.nocopy.entity.file.FileEntity;
import ru.soune.nocopy.entity.file.FileStatus;
import ru.soune.nocopy.entity.file.ProtectionStatus;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Optional;

@Repository
public interface FileEntityRepository extends JpaRepository<FileEntity, String> {
    List<FileEntity> findByUserId(Long userId);

    FileEntity findByIdAndUserId(String id, Long userId);

    Optional<FileEntity> findByUserIdAndChecksum(Long userId,  String imageHash);

    List<FileEntity> findByUserIdAndStatus(Long userId, FileStatus status);

    List<FileEntity> findByUserIdAndStatusIn(Long userId, Collection<FileStatus> statuses);

    Optional<FileEntity> findByFilePath(String filePath);

    Optional<FileEntity> findByUploadSessionId(String uploadSessionId);

    List<FileEntity> findByProtectionStatus(ProtectionStatus protectionStatus);

    boolean existsByFilePath(String filePath);

    @Query("SELECT SUM(f.fileSize) FROM FileEntity f WHERE f.userId = :userId AND f.status = 'ACTIVE'")
    Long getTotalSizeByUserId(@Param("userId") Long userId);

    @Query("SELECT f FROM FileEntity f WHERE f.protectionStatus = null AND f.status = 'ACTIVE' OR " +
            "f.protectionStatus = 'NOT_PROTECTED' AND f.status = 'ACTIVE'")
    List<FileEntity> findAllActiveFilesAndNotProtected();

    @Query("SELECT f FROM FileEntity f WHERE f.userId = :userId AND f.originalFileName LIKE %:keyword%")
    List<FileEntity> searchByFileName(@Param("userId") Long userId, @Param("keyword") String keyword);

    @Query("SELECT f FROM FileEntity f WHERE f.userId = :userId AND f.originalFileName LIKE %:keyword%")
    List<FileEntity> searchByFileNameAndStatusIn(@Param("userId") Long userId, @Param("keyword") String keyword,
                                                 List<FileStatus> statuses);

    @Query("SELECT f FROM FileEntity f WHERE f.status = :status")
    List<FileEntity> searchFileEntityByStatus(@Param("status") FileStatus status);

    @Query("SELECT f FROM FileEntity f WHERE f.id = :fileId")
    FileEntity findByFileId(@Param("fileId") String fileId);

    @Query("SELECT f FROM FileEntity f WHERE f.signature = :signature")
    FileEntity findBySignature(@Param("signature") String signature);

    @Query("SELECT f.id FROM FileEntity f WHERE f.filePath = :filePath")
    String findFileIdByFilePath(@Param("filePath") String filePath);

    List<FileEntity> findFileByUserIdAndStatus(Long userId, FileStatus status);

    @Query("SELECT f FROM FileEntity f WHERE f.mimeType = :mimeType AND f.userId IN :userIds")
    List<FileEntity> findByMimeTypeAndUserIds(String mimeType, List<Long> userIds);

    @Query("SELECT MAX(f.supportId) FROM FileEntity f")
    Integer findMaxSupportId();

    List<FileEntity> findByThumbnailPathIsNull();

    Page<FileEntity> findByStatusIn(List<FileStatus> statuses, Pageable pageable);

    List<FileEntity> findByUserIdAndStatusIn(Long userId, List<FileStatus> statuses);

    @Query("SELECT f FROM FileEntity f WHERE f.userId = :userId AND f.status IN :statuses")
    List<FileEntity> findByStatuses(Long userId, @Param("statuses") List<String> statuses);

    @Query("SELECT DISTINCT f FROM FileEntity f " +
            "LEFT JOIN FileMonitoringEntity m ON f.id = m.file.id " +
            "WHERE f.userId IN :userIds " +
            "AND f.status IN :statuses")
    Page<FileEntity> findFiles(
            @Param("userIds") List<Long> userIds,
            @Param("statuses") List<FileStatus> statuses,
            Pageable pageable);

    @Query("SELECT SUM(f.fileSize) FROM FileEntity f " +
            "WHERE f.userId IN :userIds " +
            "AND f.status IN :statuses")
    Long sumFileSize(
            @Param("userIds") List<Long> userIds,
            @Param("statuses") List<FileStatus> statuses);

    @Query("""
        SELECT new ru.soune.nocopy.dto.statistic.UserFilesStatisticResponse(
            u.fullName,
            u.Id,
            CAST(COUNT(f.id) AS int),
            COALESCE(SUM(f.fileSize), 0L)
        )
        FROM FileEntity f
        JOIN User u ON f.userId = u.Id
        WHERE 
            (:mimeType IS NULL OR :mimeType = '' OR f.mimeType LIKE CONCAT(:mimeType, '%'))
        GROUP BY u.Id, u.fullName
        ORDER BY COUNT(f.id) DESC
        LIMIT :topUsers
        """)
    List<UserFilesStatisticResponse> findTopUsers(
            @Param("topUsers") int topUsers,
            @Param("mimeType") String mimeType
    );

    @Query("""
        SELECT 
            COUNT(f.id),
            COALESCE(SUM(f.fileSize), 0)
        FROM FileEntity f
        WHERE f.protectionStatus = 'PROTECTED'
    """)
    List<Object[]> getProtectedFilesTotalStats();

    @Query(value = """
        SELECT 
            user_id,
            COUNT(*) as file_count,
            SUM(file_size) as total_size
        FROM file_entities
        WHERE protection_status = 'PROTECTED'
        GROUP BY user_id
        ORDER BY file_count
    """, nativeQuery = true)
    List<Object[]> getProtectedFilesPerUserStats();

    @Modifying
    @Transactional
    @Query("DELETE FROM FileEntity f WHERE f.id IN :ids")
    void deleteAllByIdIn(@Param("ids") List<String> ids);
}
