package com.psg.dlsysinfo.dl_sysinfo_server.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.psg.dlsysinfo.dl_sysinfo_server.dto.InstalledAppDTO;
import com.psg.dlsysinfo.dl_sysinfo_server.dto.SystemInfoDTO;
import com.psg.dlsysinfo.dl_sysinfo_server.entity.Client;
import com.psg.dlsysinfo.dl_sysinfo_server.entity.Devices;
import com.psg.dlsysinfo.dl_sysinfo_server.entity.InstalledSoftware;
import com.psg.dlsysinfo.dl_sysinfo_server.repository.ClientRepository;
import com.psg.dlsysinfo.dl_sysinfo_server.repository.InstalledSoftwareRepository;
import com.psg.dlsysinfo.dl_sysinfo_server.security.EncryptionService;
import com.psg.dlsysinfo.dl_sysinfo_server.security.JwtUtil;
import com.psg.dlsysinfo.dl_sysinfo_server.security.TokenResolver;
import com.psg.dlsysinfo.dl_sysinfo_server.service.DeviceService;
import com.psg.dlsysinfo.dl_sysinfo_server.util.NormalizationUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api")
public class SystemInfoController {

    private final JwtUtil jwtUtil;
    private final EncryptionService encryptionService;
    private final TokenResolver tokenResolver;

    public SystemInfoController(JwtUtil jwtUtil, EncryptionService encryptionService, TokenResolver tokenResolver) {
        this.jwtUtil = jwtUtil;
        this.encryptionService = encryptionService;
        this.tokenResolver = tokenResolver;
        System.out.println("🛠️ SystemInfoController created");
    }

    @Autowired private ClientRepository clientRepository;
    @Autowired private InstalledSoftwareRepository installedSoftwareRepository;
    @Autowired private DeviceService deviceService;
    @Autowired private ObjectMapper objectMapper;

    @PreAuthorize("isAuthenticated()")
    @PostMapping("/system-info")
    public ResponseEntity<Map<String, String>> receiveEncryptedData(
            @AuthenticationPrincipal Object currentUser,
            HttpServletRequest request,
            @RequestBody Map<String, String> payload) {

        Map<String, String> response = new HashMap<>();

        try {
            String token = tokenResolver.resolveToken(request);
            if (!jwtUtil.validateToken(token)) {
                return errorResponse(response, "Invalid or expired token.", 403);
            }

            String encryptedData = payload.get("data");
            if (encryptedData == null || encryptedData.isEmpty()) {
                return errorResponse(response, "No data found in request body.", 400);
            }

            String decryptedData = encryptionService.decryptData(encryptedData);
            System.out.println("🔓 Decrypted payload size: " + decryptedData.length());

            SystemInfoDTO dto;

            try {
                dto = objectMapper.readValue(decryptedData, SystemInfoDTO.class);
            } catch (Exception ex) {
                System.out.println("❌ Failed to deserialize SystemInfoDTO: " + ex.getMessage());
                ex.printStackTrace();
                return errorResponse(response, "Failed to parse payload.", 400);
            }

            if (dto.getClientIdentifier() == null || dto.getHostname() == null) {
                return errorResponse(response, "Missing clientIdentifier or hostname in payload.", 400);
            }

            String clientIdentifier = dto.getClientIdentifier();
            String hostname = dto.getHostname();
            String hashedHostname = encryptionService.hashString(hostname);

            Client client = clientRepository.findByClientIdentifier(clientIdentifier)
                    .orElseThrow(() -> new IllegalArgumentException("Client not registered"));

            Devices device = deviceService.findOrCreateDevice(hostname, hashedHostname, client);
            System.out.println("🧩 System Info: OS = " + dto.getOsName() + ", GPU = " + dto.getGpuNames());

            deviceService.populateDeviceFromDTO(device, dto);
            System.out.println("💾 Saving device: " + objectMapper.writeValueAsString(device));
            deviceService.saveDevice(device);

            List<InstalledAppDTO> installedApplications = dto.getInstalledApplications();
            if (installedApplications != null) {
                for (InstalledAppDTO app : installedApplications) {
                    if (app.getApp_name() == null) continue;

                    InstalledSoftware software = installedSoftwareRepository
                            .findByDeviceAndAppName(device, app.getApp_name())
                            .orElseGet(() -> {
                                InstalledSoftware newSoftware = new InstalledSoftware();
                                newSoftware.setDevice(device);
                                newSoftware.setAppName(app.getApp_name());
                                return newSoftware;
                            });

                    software.setAppVersion(app.getApp_version());
                    software.setPublisher(app.getPublisher());
                    software.setNormalizedAppName(NormalizationUtils.normalize(app.getApp_name()));
                    software.setNormalizedPublisher(NormalizationUtils.simplifyPublisher(app.getPublisher()));
                    software.setNormalizedProduct(NormalizationUtils.normalizeProduct(app.getApp_name()));
                    software.setLastUpdated(LocalDateTime.now());

                    installedSoftwareRepository.save(software);
                }
            }

            response.put("status", "success");
            response.put("message", "Data received and stored successfully.");
            return ResponseEntity.ok(response);

        } catch (Exception e) {
            System.out.println("❌ Unexpected exception: " + e.getMessage());
            e.printStackTrace();
            return errorResponse(response, "Server error occurred: " + e.getMessage(), 500);
        }
    }

    private ResponseEntity<Map<String, String>> errorResponse(Map<String, String> response, String message, int statusCode) {
        response.put("status", "error");
        response.put("message", message);
        return ResponseEntity.status(statusCode).body(response);
    }
}
