package com.psg.dlsysinfo.dl_sysinfo_server.service;

import com.psg.dlsysinfo.dl_sysinfo_server.entity.CachedDeviceVuln;
import com.psg.dlsysinfo.dl_sysinfo_server.entity.CveMatchResult;
import com.psg.dlsysinfo.dl_sysinfo_server.entity.CveMatchResultImpl;
import com.psg.dlsysinfo.dl_sysinfo_server.entity.Devices;
import com.psg.dlsysinfo.dl_sysinfo_server.repository.CachedDeviceVulnRepository;
import com.psg.dlsysinfo.dl_sysinfo_server.repository.DevicesRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

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

@Service
@RequiredArgsConstructor
public class CachedVulnService {

    private final VulnerabilityScannerService scannerService;
    private final DevicesRepository devicesRepository;
    private final CachedDeviceVulnRepository cachedRepo;

    @Scheduled(cron = "0 0 * * * *") // Every hour
    @Transactional
    public void refreshVulnerabilityCache() {
        System.out.println("♻️ Refreshing cached device vulnerabilities...");

        List<Devices> devices = devicesRepository.findAll();

        for (Devices device : devices) {
            Long deviceId = device.getDeviceId();

            // ✅ Get fresh matches
            List<CveMatchResult> matches = scannerService.getVulnerabilitiesForDevice(deviceId);

            // ✅ Deduplicate by CVE ID
            List<CveMatchResult> uniqueMatches = matches.stream()
                    .collect(Collectors.toMap(
                            CveMatchResult::getCveId,
                            v -> v,
                            (existing, replacement) -> existing // Keep first
                    ))
                    .values()
                    .stream()
                    .toList();

            // ✅ Delete old entries first, then flush to commit
            cachedRepo.deleteByDeviceId(deviceId);
            cachedRepo.flush();

            // ✅ Save deduplicated matches
            List<CachedDeviceVuln> toSave = uniqueMatches.stream()
                    .map(v -> CachedDeviceVuln.builder()
                            .deviceId(deviceId)
                            .cveId(v.getCveId())
                            .severity(v.getSeverity())
                            .score(v.getScore())
                            .description(v.getDescription())
                            .affectedApp(v.getAffectedApp())
                            .installedVersion(v.getInstalledVersion())
                            .lastModified(v.getLastModifiedDate())
                            .lastUpdated(LocalDateTime.now())
                            .lenientMatch(v instanceof CveMatchResultImpl impl && impl.isLenientMatch())
                            .build())
                    .toList();

            cachedRepo.saveAll(toSave);

            System.out.printf("📦 Device ID %d matched %d vulnerabilities (saved %d after dedup)%n",
                    deviceId, matches.size(), toSave.size());
        }

        System.out.println("✅ Vulnerability cache refreshed.");
    }


    public String refreshVulnerabilityCacheWithSummary() {
        StringBuilder sb = new StringBuilder();
        List<Devices> devices = devicesRepository.findAll();

        for (Devices device : devices) {
            List<CveMatchResult> vulns = scannerService.getVulnerabilitiesForDevice(device.getDeviceId());
            cachedRepo.deleteByDeviceId(device.getDeviceId());

            List<CveMatchResult> uniqueMatches = vulns.stream()
                    .collect(Collectors.toMap(
                            CveMatchResult::getCveId,
                            v -> v,
                            (existing, replacement) -> existing
                    ))
                    .values()
                    .stream()
                    .toList();

            List<CachedDeviceVuln> toSave = uniqueMatches.stream()
                    .map(v -> CachedDeviceVuln.builder()
                            .deviceId(device.getDeviceId())
                            .cveId(v.getCveId())
                            .severity(v.getSeverity())
                            .score(v.getScore())
                            .description(v.getDescription())
                            .affectedApp(v.getAffectedApp())
                            .installedVersion(v.getInstalledVersion())
                            .lastModified(v.getLastModifiedDate())
                            .lastUpdated(LocalDateTime.now())
                            .lenientMatch(v instanceof CveMatchResultImpl impl && impl.isLenientMatch())
                            .build())
                    .toList();


            System.out.printf("📦 Saving %d vulns for device %d\n", toSave.size(), device.getDeviceId());
            cachedRepo.saveAll(toSave);
            System.out.println("✅ Save call completed.");

            sb.append(String.format("📦 Device %d: %d vulns\n", device.getDeviceId(), vulns.size()));
        }

        return sb.toString();
    }

}



