package research.loghunter.service;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import research.loghunter.dto.ServerDto;
import research.loghunter.dto.ServerLogPathDto;
import research.loghunter.entity.Server;
import research.loghunter.entity.ServerLogPath;
import research.loghunter.repository.ServerRepository;
import research.loghunter.util.CryptoUtil;

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

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ServerService {

    private final ServerRepository serverRepository;
    private final CryptoUtil cryptoUtil;

    public List<ServerDto> findAll() {
        return serverRepository.findAll().stream()
                .map(this::toDto)
                .collect(Collectors.toList());
    }

    public List<ServerDto> findAllActive() {
        return serverRepository.findByActiveTrueOrderByNameAsc().stream()
                .map(this::toDto)
                .collect(Collectors.toList());
    }

    public ServerDto findById(Long id) {
        return serverRepository.findById(id)
                .map(this::toDto)
                .orElseThrow(() -> new RuntimeException("Server not found: " + id));
    }

    @Transactional
    public ServerDto create(ServerDto dto) {
        Server server = toEntity(dto);
        server = serverRepository.save(server);
        return toDto(server);
    }

    @Transactional
    public ServerDto update(Long id, ServerDto dto) {
        Server server = serverRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Server not found: " + id));

        server.setName(dto.getName());
        server.setHost(dto.getHost());
        server.setPort(dto.getPort());
        server.setUsername(dto.getUsername());
        server.setAuthType(dto.getAuthType());
        server.setActive(dto.getActive());

        if ("PASSWORD".equals(dto.getAuthType())) {
            if (dto.getPassword() != null && !dto.getPassword().isEmpty()) {
                server.setEncryptedPassword(cryptoUtil.encrypt(dto.getPassword()));
            }
            server.setKeyFilePath(null);
            server.setEncryptedPassphrase(null);
        } else if ("KEY_FILE".equals(dto.getAuthType())) {
            server.setKeyFilePath(dto.getKeyFilePath());
            if (dto.getPassphrase() != null && !dto.getPassphrase().isEmpty()) {
                server.setEncryptedPassphrase(cryptoUtil.encrypt(dto.getPassphrase()));
            }
            server.setEncryptedPassword(null);
        }

        return toDto(serverRepository.save(server));
    }

    @Transactional
    public void delete(Long id) {
        serverRepository.deleteById(id);
    }

    @Transactional
    public void updateLastScanAt(Long id) {
        serverRepository.findById(id).ifPresent(server -> {
            server.setLastScanAt(java.time.LocalDateTime.now());
            serverRepository.save(server);
        });
    }

    @Transactional
    public void updateLastErrorAt(Long id) {
        serverRepository.findById(id).ifPresent(server -> {
            server.setLastErrorAt(java.time.LocalDateTime.now());
            serverRepository.save(server);
        });
    }

    public String getDecryptedPassword(Long id) {
        Server server = serverRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Server not found: " + id));
        return cryptoUtil.decrypt(server.getEncryptedPassword());
    }

    public String getDecryptedPassphrase(Long id) {
        Server server = serverRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Server not found: " + id));
        return cryptoUtil.decrypt(server.getEncryptedPassphrase());
    }

    private ServerDto toDto(Server server) {
        return ServerDto.builder()
                .id(server.getId())
                .name(server.getName())
                .host(server.getHost())
                .port(server.getPort())
                .username(server.getUsername())
                .authType(server.getAuthType())
                .keyFilePath(server.getKeyFilePath())
                .active(server.getActive())
                .lastScanAt(server.getLastScanAt())
                .lastErrorAt(server.getLastErrorAt())
                .createdAt(server.getCreatedAt())
                .logPaths(server.getLogPaths().stream()
                        .map(this::toLogPathDto)
                        .collect(Collectors.toList()))
                .build();
    }

    private ServerLogPathDto toLogPathDto(ServerLogPath logPath) {
        return ServerLogPathDto.builder()
                .id(logPath.getId())
                .serverId(logPath.getServer().getId())
                .path(logPath.getPath())
                .filePattern(logPath.getFilePattern())
                .description(logPath.getDescription())
                .active(logPath.getActive())
                .build();
    }

    private Server toEntity(ServerDto dto) {
        Server server = Server.builder()
                .name(dto.getName())
                .host(dto.getHost())
                .port(dto.getPort() != null ? dto.getPort() : 22)
                .username(dto.getUsername())
                .authType(dto.getAuthType())
                .active(dto.getActive() != null ? dto.getActive() : true)
                .build();

        if ("PASSWORD".equals(dto.getAuthType()) && dto.getPassword() != null) {
            server.setEncryptedPassword(cryptoUtil.encrypt(dto.getPassword()));
        } else if ("KEY_FILE".equals(dto.getAuthType())) {
            server.setKeyFilePath(dto.getKeyFilePath());
            if (dto.getPassphrase() != null) {
                server.setEncryptedPassphrase(cryptoUtil.encrypt(dto.getPassphrase()));
            }
        }

        return server;
    }
}
