package ru.soune.nocopy.service.dockview;

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.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.soune.nocopy.dto.docviewer.DocViewerRequest;
import ru.soune.nocopy.dto.docviewer.DockViewResponse;
import ru.soune.nocopy.dto.docviewer.DockViewStatsResponse;
import ru.soune.nocopy.entity.docviewer.DockView;
import ru.soune.nocopy.entity.file.FileEntity;
import ru.soune.nocopy.entity.user.User;
import ru.soune.nocopy.repository.DockViewRepository;
import ru.soune.nocopy.repository.FileEntityRepository;
import ru.soune.nocopy.repository.UserRepository;
import ru.soune.nocopy.service.geo.GeoCountryService;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
public class DockViewService {

    private final DockViewRepository dockViewRepository;

    private final GeoCountryService geoCountryService;

    private final FileEntityRepository fileEntityRepository;

    private final UserRepository userRepository;

    @Transactional
    public DockView createDockView(DocViewerRequest request, Long userId) {
        DockView dockView = new DockView();
        dockView.setViewerId(userId);
        dockView.setViewerIp(request.getIp() != null ? request.getIp() : "-");
        dockView.setUserAgent(request.getUserAgent() != null ? request.getUserAgent() : "-");
        dockView.setFileId(request.getFileId());
        dockView.setCreatedAt(LocalDateTime.now());

        DockView saved = dockViewRepository.save(dockView);

        log.info("Created dock view record: fileId={}, viewerId={}, ip={}",
                saved.getFileId(), saved.getViewerId(), saved.getViewerIp());

        return saved;
    }

    public DockViewStatsResponse getTotalStatsByFileIds(List<String> fileIds) {
        List<Object[]> statsList = dockViewRepository.findTotalViewsStatsByFileIds(fileIds);
        List<String> allViewerIps = dockViewRepository.findAllViewerIps(fileIds);
        Map<String, Integer> uniqueGeoCount = geoCountryService.getUniqueGeoCount(allViewerIps);

        if (statsList.isEmpty()) {
            return DockViewStatsResponse.builder()
                    .totalViews(0L)
                    .uniqueIps(0L)
                    .uniqueUsers(0L)
                    .firstViewDate(null)
                    .lastViewDate(null)
                    .build();
        }

        Object[] stats = statsList.get(0);

        return DockViewStatsResponse.builder()
                .totalViews((Long) stats[0])
                .uniqueIps((Long) stats[1])
                .uniqueUsers((Long) stats[2])
                .documentsCount(fileIds.size())
                .uniqueCityCount(uniqueGeoCount.get("uniqueCities"))
                .uniqueCountryCount(uniqueGeoCount.get("uniqueCountries"))
                .lastViewDate((LocalDateTime) stats[3])
                .firstViewDate((LocalDateTime) stats[4])
                .build();
    }

    public List<DockViewStatsResponse> getStatsByFileIds(List<String> fileIds) {
        List<Object[]> statsList = dockViewRepository.findViewsStatsByFileIds(fileIds);

        Map<String, Object[]> statsMap = statsList.stream()
                .collect(Collectors.toMap(
                        stats -> (String) stats[0],
                        stats -> stats
                ));

        return fileIds.stream()
                .map(fileId -> {
                    Object[] stats = statsMap.get(fileId);

                    if (stats == null) {
                        return DockViewStatsResponse.builder()
                                .fileId(fileId)
                                .totalViews(0L)
                                .uniqueIps(0L)
                                .uniqueUsers(0L)
                                .firstViewDate(null)
                                .lastViewDate(null)
                                .build();
                    }

                    return DockViewStatsResponse.builder()
                            .fileId((String) stats[0])
                            .totalViews((Long) stats[1])
                            .uniqueIps((Long) stats[2])
                            .uniqueUsers((Long) stats[3])
                            .lastViewDate((LocalDateTime) stats[4])
                            .firstViewDate((LocalDateTime) stats[5])
                            .build();
                })
                .collect(Collectors.toList());
    }

    public DockViewStatsResponse getOpenLastHistory(int page, int size, String sortDirection, String sortBy,
                                                     List<String> fileIds) {
        if (sortDirection == null || sortDirection.isEmpty()) {
            sortDirection = "desc";
        }

        if (sortBy == null || sortBy.isEmpty()) {
            sortBy = "createdAt";
        }

        if (page < 0) {
            page = 0;
        }
        if (size < 1) {
            size = 10;
        }

        Pageable pageable = PageRequest.of(page, size,
                Sort.by(Sort.Direction.fromString(sortDirection), sortBy));

        Page<DockView> dockViews = dockViewRepository.findAllByFileIds(fileIds, pageable);

        if (dockViews.isEmpty()) {
            return DockViewStatsResponse.builder()
                    .build();
        }

        List<DockViewResponse> history = new ArrayList<>();

        for (DockView dockView: dockViews) {
            String country = geoCountryService.getCountryByIp(dockView.getViewerIp());
            String city = geoCountryService.getCityByIp(dockView.getViewerIp());
            LocalDateTime createdAt = dockView.getCreatedAt();
            String fileId = dockView.getFileId();

            FileEntity file = fileEntityRepository.findByFileId(fileId);
            Long viewerId = dockView.getViewerId();
            User viewer =  userRepository.findById(viewerId).orElse(null);

            DockViewResponse response = DockViewResponse.builder()
                    .city(city)
                    .country(country)
                    .viewDate(createdAt)
                    .document(file.getOriginalFileName())
                    .viewer(viewer == null ? "guest" : viewer.getFullName())
                    .build();

            history.add(response);
        }

        return DockViewStatsResponse.builder()
                .history(history)
                .pageNumber(dockViews.getNumber())
                .pageSize(dockViews.getSize())
                .totalPages(dockViews.getTotalPages())
                .totalElements(dockViews.getTotalElements())
                .build();
    }

    public DockViewStatsResponse getBasicStatsByFileId(String fileId) {
        long totalViews = dockViewRepository.countByFileId(fileId);
        long uniqueIps = dockViewRepository.countUniqueIpsByFileId(fileId);

        return DockViewStatsResponse.builder()
                .fileId(fileId)
                .totalViews(totalViews)
                .uniqueIps(uniqueIps)
                .build();
    }

    public DockViewStatsResponse getAllFilesStatsPaginated(Pageable pageable) {
        Page<Object[]> statsPage = dockViewRepository.findViewsStatsPaginated(pageable);

        List<DockViewStatsResponse> statsList = statsPage.getContent().stream()
                .map(row -> DockViewStatsResponse.builder()
                        .fileId((String) row[0])
                        .totalViews((Long) row[1])
                        .uniqueIps((Long) row[2])
                        .lastViewDate((LocalDateTime) row[3])
                        .build())
                .collect(Collectors.toList());

        return DockViewStatsResponse.builder()
                .content(statsList)
                .pageNumber(statsPage.getNumber())
                .pageSize(statsPage.getSize())
                .totalElements(statsPage.getTotalElements())
                .totalPages(statsPage.getTotalPages())
                .isLast(statsPage.isLast())
                .isFirst(statsPage.isFirst())
                .hasNext(statsPage.hasNext())
                .hasPrevious(statsPage.hasPrevious())
                .build();
    }

    public boolean hasViewedFromIp(String fileId, String ip) {
        return dockViewRepository.existsByFileIdAndViewerIp(fileId, ip);
    }

    public Optional<LocalDateTime> getLastViewDate(String fileId) {
        return dockViewRepository.findLastViewDateByFileId(fileId);
    }

    @Transactional
    public void deleteAllViewsByFileId(String fileId) {
        dockViewRepository.deleteAllByFileId(fileId);
        log.info("Deleted all views for fileId: {}", fileId);
    }

    @Transactional
    public void cleanOldViews() {
        LocalDateTime thirtyDaysAgo = LocalDateTime.now().minusDays(30);
        dockViewRepository.deleteAllByCreatedAtBefore(thirtyDaysAgo);
        log.info("Cleaned views older than: {}", thirtyDaysAgo);
    }
}
