package ru.soune.nocopy.service.monitoring;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.soune.nocopy.dto.monitoring.MonitoringStatusResponse;
import ru.soune.nocopy.dto.tarriff.TariffDTO;
import ru.soune.nocopy.entity.file.*;
import ru.soune.nocopy.entity.monitoring.FileMonitoringEntity;
import ru.soune.nocopy.entity.monitoring.MonitoringType;
import ru.soune.nocopy.entity.tarif.TariffInfo;
import ru.soune.nocopy.entity.tarif.TariffType;
import ru.soune.nocopy.entity.user.User;
import ru.soune.nocopy.exception.UserNotHavePermission;
import ru.soune.nocopy.repository.FileEntityRepository;
import ru.soune.nocopy.repository.FileMonitoringRepository;
import ru.soune.nocopy.repository.UserRepository;
import ru.soune.nocopy.service.tariff.TariffService;

import java.io.FileNotFoundException;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;

@Slf4j
@Service
@RequiredArgsConstructor
public class FileMonitoringService {
    private final FileMonitoringRepository monitoringRepository;

    private final FileEntityRepository fileRepository;

    private final TariffService tariffService;

    private final UserRepository userRepository;

    @Transactional
    public FileMonitoringEntity setMonitoring(Long userId, String fileId, MonitoringType type) throws FileNotFoundException {
        FileEntity file = fileRepository.findById(fileId)
                .orElseThrow(() -> new FileNotFoundException(fileId));

        if (!file.getUserId().equals(userId)) {
            throw new UserNotHavePermission("User not Have permission for file");
        }

        FileMonitoringEntity monitoring = monitoringRepository.findByFileId(fileId)
                .orElse(FileMonitoringEntity.builder()
                        .file(file)
                        .userId(userId)
                        .build());

        monitoring.setMonitoringType(type);
        monitoring.setActive(type != MonitoringType.NONE);
        monitoring.setNextRun(calculateNextRun(type, LocalDateTime.now()));
        monitoring.setUpdatedAt(LocalDateTime.now());

        if (monitoring.getCreatedAt() == null) {
            monitoring.setCreatedAt(LocalDateTime.now());
        }

        return monitoringRepository.save(monitoring);
    }

    @Transactional
    public void updateNextRun(FileMonitoringEntity monitoring) {
        if (monitoring.isActive() && monitoring.getMonitoringType() != MonitoringType.NONE) {
            LocalDateTime nextRun = calculateNextRun(monitoring.getMonitoringType(), LocalDateTime.now());
            monitoring.setNextRun(nextRun);
            monitoringRepository.save(monitoring);
        }
    }


    private LocalDateTime calculateNextRun(MonitoringType type, LocalDateTime from) {
        LocalDateTime nextRun = from.withHour(2).withMinute(0).withSecond(0).withNano(0);

        if (from.isAfter(nextRun)) {
            nextRun = nextRun.plusDays(1);
        }

        return switch (type) {
            case MONITORING_DAILY -> nextRun;
            case MONITORING_WEEKLY -> nextRun.with(TemporalAdjusters.nextOrSame(java.time.DayOfWeek.MONDAY));
            case MONITORING_MONTHLY -> nextRun.with(TemporalAdjusters.firstDayOfNextMonth());
            case NONE -> null;
        };
    }

    public MonitoringStatusResponse getMonitoringStatus(String fileId, Long userId) {
        FileMonitoringEntity monitoring = monitoringRepository.findByFileId(fileId).orElse(null);
        FileEntity file = fileRepository.findById(fileId).orElseThrow();
        User user = userRepository.findById(userId).orElseThrow();

        return MonitoringStatusResponse.builder()
                .fileId(fileId)
                .fileName(file.getOriginalFileName())
                .monitoringType(monitoring != null ? monitoring.getMonitoringType() : MonitoringType.NONE)
                .nextRun(monitoring != null ? monitoring.getNextRun() : null)
                .lastRun(monitoring != null ? monitoring.getLastRun() : null)
                .lastRunStatus(monitoring != null ? monitoring.getLastRunStatus() : null)
                .isActive(monitoring != null && monitoring.isActive())
                .needAddTokens(checkTokensForMonitoring(user, monitoring.getMonitoringType()))
                .build();
    }

    public boolean checkTokensForMonitoring(User user, MonitoringType monitoringType) {
        if (monitoringType == MonitoringType.NONE) {
            return false;
        }

        TariffDTO tariff = tariffService.getTariffByType(TariffType.valueOf(monitoringType.name()));
        TariffInfo activeTariffInfo = user.getActiveTariffInfo();
        int currentUserCountTokens = activeTariffInfo.getTokens() + activeTariffInfo.getBoughtTokens();

        return currentUserCountTokens > tariff.getTokens();
    }
}
