package com.psg.dlsysinfo.dl_sysinfo_server.service;

import com.psg.dlsysinfo.dl_sysinfo_server.dto.*;
import com.psg.dlsysinfo.dl_sysinfo_server.entity.InstalledSoftware;
import com.psg.dlsysinfo.dl_sysinfo_server.repository.CveRepository;
import com.psg.dlsysinfo.dl_sysinfo_server.repository.DevicesRepository;
import com.psg.dlsysinfo.dl_sysinfo_server.repository.InstalledSoftwareRepository;
import com.psg.dlsysinfo.dl_sysinfo_server.security.EncryptionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
public class SoftwareService {

    @Autowired
    private InstalledSoftwareRepository installedSoftwareRepository;

    @Autowired
    private DevicesRepository devicesRepository;

    @Autowired
    private CveRepository cveRepository;

    @Autowired
    private EncryptionService encryptionService;

    @Transactional(readOnly = true)
    public List<SoftwareSummaryDTO> getSoftwareOverviewForClient(Long clientId) {
        List<InstalledSoftware> softwareList = installedSoftwareRepository.findAllByClientId(clientId);
        return mapToSoftwareSummary(softwareList);
    }

    @Transactional(readOnly = true)
    public List<SoftwareSummaryDTO> getSoftwareOverviewForAllClients() {
        List<InstalledSoftware> softwareList = installedSoftwareRepository.findAllWithDeviceAndClient();
        return mapToSoftwareSummary(softwareList);
    }

    private List<SoftwareSummaryDTO> mapToSoftwareSummary(List<InstalledSoftware> softwareList) {
        System.out.println("📊 Mapping software summary for " + softwareList.size() + " entries");

        // Step 1: Get distinct app names for batch lookup
        List<String> productNames = softwareList.stream()
                .map(InstalledSoftware::getAppName)
                .filter(name -> name != null && !name.isBlank())
                .map(String::toLowerCase)
                .distinct()
                .toList();

        // Step 2: Batch-fetch CVEs
        List<ProductCveDTO> cveList = cveRepository.findAllProductMatches(productNames);

        // 🔥 Map to DeviceVulnerabilityDTO
        Map<String, List<DeviceVulnerabilityDTO>> cveMap = cveList.stream()
                .collect(Collectors.groupingBy(
                        dto -> dto.getProduct().toLowerCase(),
                        Collectors.mapping(
                                dto -> new DeviceVulnerabilityDTO(
                                        dto.getCveId(),
                                        dto.getDescription(),    // using description as "title"
                                        dto.getSeverity(),
                                        null,                    // no score available in ProductCveDTO
                                        null,                     // no published date either
                                        null
                                ),
                                Collectors.toList()
                        )
                ));

        System.out.println("🛡️ CVEs fetched: " + cveList.size());

        // Step 3: Group software entries
        Map<String, List<SoftwareInstanceDTO>> softwareMap = new HashMap<>();

        for (InstalledSoftware sw : softwareList) {
            String hostname;
            try {
                hostname = encryptionService.decryptData(sw.getDevice().getEncryptedHostname());
            } catch (Exception e) {
                hostname = "DECRYPTION_FAILED";
            }

            String appName = sw.getAppName();
            if (appName == null || appName.isBlank()) continue;

            softwareMap.putIfAbsent(appName, new ArrayList<>());

            SoftwareInstanceDTO instance = new SoftwareInstanceDTO();
            instance.setId(sw.getId());
            instance.setDeviceId(sw.getDevice().getDeviceId());
            instance.setHostname(hostname);
            instance.setVersion(sw.getAppVersion());
            instance.setPublisher(sw.getPublisher());
            instance.setLastUpdated(sw.getLastUpdated());

            String key = appName.toLowerCase();
            instance.setVulnerabilities(cveMap.getOrDefault(key, List.of())); // ✅ CORRECT now

            softwareMap.get(appName).add(instance);
        }

        System.out.println("✅ Done mapping software entries");

        // Step 4: Format for frontend
        return softwareMap.entrySet().stream().map(entry -> {
            SoftwareSummaryDTO summary = new SoftwareSummaryDTO();
            summary.setSoftwareName(entry.getKey());
            summary.setInstances(entry.getValue());
            summary.setTotalDevices(entry.getValue().size());
            return summary;
        }).toList();
    }

}
