package com.eactive.testmaster.socket.service;

import com.eactive.testmaster.socket.NettyServer;
import com.eactive.testmaster.socket.entity.NettyServerEntity;
import com.eactive.testmaster.socket.repository.NettyServerRepository;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.persistence.EntityNotFoundException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
public class NettyServerService {
    private final NettyServerRepository repository;
    private final Map<Integer, NettyServer> activeServers = new HashMap<>();

    public NettyServerService(NettyServerRepository repository) {
        this.repository = repository;
    }

    @Transactional(readOnly = true)
    public Page<NettyServerEntity> findAll(Specification<NettyServerEntity> spec, Pageable pageable) {
        return repository.findAll(spec, pageable);
    }

    @Transactional(readOnly = true)
    public NettyServerEntity findById(Long id) {
        return repository.findById(id)
            .orElseThrow(() -> new EntityNotFoundException("Server not found with id: " + id));
    }

    public NettyServerEntity createServer(NettyServerEntity server) {
        validatePort(server.getPort());
        server.setActive(true);
        return repository.save(server);
    }

    public NettyServerEntity updateServer(Long id, NettyServerEntity updatedServer) {
        NettyServerEntity existingServer = findById(id);

        // If port is being changed, validate the new port
        if (!existingServer.getPort().equals(updatedServer.getPort())) {
            validatePort(updatedServer.getPort());

            // Stop the server if it's running on the old port
            try {
                stopServerIfRunning(existingServer.getPort());
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Failed to stop server during port update", e);
            }
        }

        // Update all fields
        existingServer.setPort(updatedServer.getPort());
        existingServer.setActive(updatedServer.getActive());
        existingServer.setName(updatedServer.getName());
        existingServer.setScript(updatedServer.getScript());  // Add this line to update the script

        return repository.save(existingServer);
    }

    public void deleteServer(Long id) throws InterruptedException {
        NettyServerEntity server = findById(id);
        stopServerIfRunning(server.getPort());
        repository.deleteById(id);
    }

    public void startServer(Long id) throws InterruptedException {
        NettyServerEntity entity = findById(id);

        if (!entity.getActive()) {
            throw new IllegalStateException("Cannot start inactive server: " + id);
        }

        if (!activeServers.containsKey(entity.getPort())) {
            NettyServer server = new NettyServer(entity.getPort(), entity.getScript());
            server.start();
            activeServers.put(entity.getPort(), server);
        }
    }

    @Transactional
    public void stopServer(Long id) throws InterruptedException {
        NettyServerEntity entity = findById(id);
        stopServerIfRunning(entity.getPort());
    }

    private void stopServerIfRunning(Integer port) throws InterruptedException {
        NettyServer server = activeServers.remove(port);
        if (server != null) {
            server.stop();
        }
    }

//    @Transactional
//    public void deactivateServer(Long id) throws InterruptedException {
//        NettyServerEntity entity = findById(id);
//        stopServerIfRunning(entity.getPort());
//        entity.setActive(false);
//        repository.save(entity);
//    }

//    @Transactional(readOnly = true)
//    public List<NettyServerEntity> getAllServers() {
//        return repository.findAll();
//    }

//    @Transactional(readOnly = true)
//    public List<NettyServerEntity> getActiveServers() {
//        return repository.findByActive(true);
//    }

//    @Transactional(readOnly = true)
//    public boolean isServerRunning(Integer port) {
//        return activeServers.containsKey(port);
//    }

//    @Transactional(readOnly = true)
//    public Map<Integer, Boolean> getServerStatuses(List<Integer> ports) {
//        Map<Integer, Boolean> statuses = new HashMap<>();
//        for (Integer port : ports) {
//            statuses.put(port, activeServers.containsKey(port));
//        }
//        return statuses;
//    }

    private void validatePort(Integer port) {
        if (port == null) {
            throw new IllegalArgumentException("Port cannot be null");
        }

        if (port < 1024 || port > 65535) {
            throw new IllegalArgumentException("Port must be between 1024 and 65535");
        }

        if (port >= 5701 && port <= 5801) {
            throw new IllegalArgumentException(
                "Port " + port + " conflicts with Hazelcast port range (5701-5801)"
            );
        }

        Optional<NettyServerEntity> existingServer = repository.findByPort(port);
        if (existingServer.isPresent()) {
            throw new IllegalArgumentException("Port " + port + " is already in use");
        }
    }
}
