package ru.dragonestia.kubik.discovery.controller;

import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import ru.dragonestia.kubik.discovery.component.UpdateStateNotifier;
import ru.dragonestia.kubik.discovery.service.ServerService;
import ru.dragonestia.kubik.dto.ServerData;
import ru.dragonestia.kubik.dto.ServerRegistrationData;

@Log4j2
@RequiredArgsConstructor
@RequestMapping("/api/v1/server")
@RestController
public class ServerController {

    private final ServerService serverService;
    private final UpdateStateNotifier updateStateNotifier;

    @PostMapping
    ServerRegistrationData registerServer(
            @RequestParam String groupId,
            @RequestParam int port,
            @RequestParam(required = false, defaultValue = "true") boolean canPicked,
            HttpServletRequest request
    ) {
        return registerServer(groupId, request.getRemoteAddr(), port, canPicked);
    }

    @PostMapping("/direct")
    ServerRegistrationData registerServer(
            @RequestParam String groupId,
            @RequestParam String address,
            @RequestParam int port,
            @RequestParam(required = false, defaultValue = "true") boolean canPicked
    ) {
        var server = serverService.register(groupId, address, port);
        server.setCanPicked(canPicked);
        return new ServerRegistrationData(server.getId());
    }

    @DeleteMapping
    void unregisterServer(@RequestParam String id) {
        serverService.unregister(id);
    }

    @GetMapping
    ServerData pickServer(@RequestParam String groupId) {
        var server = serverService.pick(groupId);
        if (server == null) return new ServerData(null);
        return new ServerData(server.getId());
    }

    @PutMapping("/can-picked")
    void updateCanPicked(
            @RequestParam String id,
            @RequestParam boolean value
    ) {
        serverService.find(id).ifPresent(server -> server.setCanPicked(value));
    }

    @GetMapping("/updates")
    SseEmitter updateStateStream(HttpServletRequest request) {
        var emitter = new SseEmitter(-1L);
        var subscriber = updateStateNotifier.subscribe(response -> {
            try {
                emitter.send(response);
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
        });
        emitter.onTimeout(() -> {
            log.warn("Timeout SSE connection with {}:{}", request.getRemoteAddr(), request.getRemotePort());
            subscriber.dispose();
        });
        emitter.onError(throwable -> {
            log.error("Failed send update state for {}:{}. Reason: {}, {}",
                    request.getRemoteAddr(),
                    request.getRemotePort(),
                    throwable.getClass().getSimpleName(),
                    throwable.getMessage());
            subscriber.dispose();
        });
        return emitter;
    }
}
