package com.psg.dlsysinfo.dl_sysinfo_server.controller;

import com.psg.dlsysinfo.dl_sysinfo_server.dto.*;
import com.psg.dlsysinfo.dl_sysinfo_server.entity.CachedInstalledSoftware;
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.CachedInstalledSoftwareRepository;
import com.psg.dlsysinfo.dl_sysinfo_server.repository.DevicesRepository;
import com.psg.dlsysinfo.dl_sysinfo_server.security.CurrentUser;
import com.psg.dlsysinfo.dl_sysinfo_server.security.EncryptionService;
import com.psg.dlsysinfo.dl_sysinfo_server.service.CachedVulnService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
@RequestMapping("/api/cached")
@RequiredArgsConstructor
public class CacheController {

    private final DevicesRepository devicesRepository;
    private final CachedDeviceVulnRepository cachedRepo;
    private final EncryptionService encryptionService;
    private final CachedVulnService cachedVulnService;
    private final CachedInstalledSoftwareRepository cachedSoftwareRepo;

    @GetMapping("/devices/with-vulns")
    public DeviceWithCveResponse getDevicesWithCachedVulns(@AuthenticationPrincipal CurrentUser user) {
        if (user == null) {
            throw new IllegalArgumentException("Authenticated user context is missing");
        }

        List<Devices> devices = devicesRepository.findByClient_ClientIdentifier(user.getClientIdentifier());
        return buildDeviceWithCveResponse(devices);
    }


    @GetMapping("/devices/with-vulns/all")
    public DeviceWithCveResponse getAllDevicesWithCachedVulns(@AuthenticationPrincipal CurrentUser user) {
        if (user == null) {
            throw new IllegalArgumentException("Authenticated user context is missing");
        }

        if (!user.hasRole("ADMIN")) {
            throw new SecurityException("Access denied: ADMIN role required");
        }

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


    private DeviceWithCveResponse buildDeviceWithCveResponse(List<Devices> devices) {
        List<DetailedDeviceDTO> detailedDevices = devices.stream().map(device -> {
            DetailedDeviceDTO dto = new DetailedDeviceDTO();
            dto.setDeviceId(device.getDeviceId());
            dto.setLastCheckedIn(device.getLastCheckedIn());
            dto.setOsName(device.getOsName());
            dto.setOsVersion(device.getOsVersion());
            dto.setWindowsVersion(device.getWindowsVersion());
            dto.setWindowsBuild(device.getWindowsBuild());
            dto.setOsArchitecture(device.getOsArchitecture());
            dto.setProcessorName(device.getProcessorName());
            dto.setProcessorArchitecture(device.getProcessorArchitecture());
            dto.setGpuNames(device.getGpuName() != null
                    ? Arrays.stream(device.getGpuName().split(",")).map(String::trim).toList()
                    : Collections.emptyList());
            dto.setTotalMemory(device.getTotalMemory());
            dto.setLastBootTime(device.getLastBootTime());

            try {
                dto.setHostname(encryptionService.decryptData(device.getEncryptedHostname()));
                if (device.getClient() != null && device.getClient().getClientNameEncrypted() != null) {
                    dto.setClientName(encryptionService.decryptData(device.getClient().getClientNameEncrypted()));
                }
            } catch (Exception e) {
                dto.setHostname("DECRYPTION_FAILED");
                dto.setClientName("DECRYPTION_FAILED");
            }


            dto.setDrives(device.getDrives().stream().map(d ->
                            new DriveInfoDTO(d.getName(), d.getDriveType(), d.getTotalSizeGB(), d.getFreeSpaceGB()))
                    .toList());

            dto.setInstalledApplications(device.getInstalledApplications().stream().map(app ->
                            new InstalledAppDTO(app.getAppName(), app.getAppVersion(), app.getPublisher()))
                    .toList());

            dto.setIpAddresses(device.getIpAddresses().stream().map(ip ->
                            new IpAddressDTO(ip.getInterfaceName(), ip.getIpAddress(), ip.getMacAddress()))
                    .toList());

            return dto;
        }).toList();

        Map<Long, List<DeviceVulnerabilityDTO>> vulnMap = new HashMap<>();
        for (DetailedDeviceDTO device : detailedDevices) {
            List<DeviceVulnerabilityDTO> vulns = cachedRepo.findByDeviceId(device.getDeviceId()).stream()
                    .map(v -> new DeviceVulnerabilityDTO(
                            v.getCveId(),                                          // ✅ CVE ID
                            v.getDescription(),                                    // ✅ Description
                            v.getSeverity(),                                       // ✅ Severity
                            v.getScore(),                                          // ✅ Score
                            v.getLastModified() != null ? v.getLastModified().toString() : null,   // ✅ Last Modified
                            v.getLastModified() != null ? v.getLastModified().toString() : null    // ✅ Also use for Published
                    ))



                    .toList();
            vulnMap.put(device.getDeviceId(), vulns);
        }

        return new DeviceWithCveResponse(detailedDevices, vulnMap);
    }


    @GetMapping("/software/summary")
    public List<CachedInstalledSoftware> getSoftwareSummaryForClient(@AuthenticationPrincipal CurrentUser user) {
        if (user == null) {
            throw new IllegalArgumentException("Authenticated user context is missing");
        }

        List<CachedInstalledSoftware> entries;

        if (user.hasRole("ADMIN")) {
            System.out.println("🔓 Admin fetching all cached installed software");
            entries = cachedSoftwareRepo.findAll();
        } else {
            List<Long> deviceIds = devicesRepository.findByClient_ClientIdentifier(user.getClientIdentifier())
                    .stream()
                    .map(Devices::getDeviceId)
                    .toList();

            System.out.println("🔐 User fetching cached software for devices: " + deviceIds);

            entries = cachedSoftwareRepo.findAll().stream()
                    .filter(entry -> deviceIds.contains(entry.getDeviceId()))
                    .toList();
        }

        // ✨ Decrypt using encryptedHostname instead of trying to decrypt the hash
        List<CachedInstalledSoftware> decryptedEntries = entries.stream()
                .map(entry -> {
                    try {
                        // Decrypt using encryptedHostname
                        if (entry.getEncryptedHostname() != null) {
                            entry.setHostname(encryptionService.decryptData(entry.getEncryptedHostname()));
                        } else {
                            entry.setHostname("MISSING_ENCRYPTED_HOSTNAME");
                        }
                    } catch (Exception e) {
                        entry.setHostname("DECRYPTION_FAILED");
                    }
                    return entry;
                })
                .toList();

        return decryptedEntries;
    }


}
