package com.lanyuanxiaoyao.service.queue.controller;

import com.lanyuanxiaoyao.service.configuration.entity.queue.QueueItem;
import com.lanyuanxiaoyao.service.queue.configuration.Constants;
import io.micrometer.core.instrument.MeterRegistry;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.*;

/**
 * @author lanyuanxiaoyao
 * @date 2023-05-06
 */
@RestController
@RequestMapping("queue")
public class QueueController extends QueueOperator {
    private static final Logger logger = LoggerFactory.getLogger(QueueController.class);
    private final MeterRegistry meterRegistry;

    public QueueController(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }

    private PriorityBlockingQueue<QueueItem<?>> generateDeque(String name) {
        return new PriorityBlockingQueue<>();
    }

    private void makeVoid(String name, Consumer<PriorityBlockingQueue<QueueItem<?>>> consumer) {
        consumer.accept(Constants.QUEUE_MAP.computeIfAbsent(name, this::generateDeque));
    }

    private <T> T makeReturn(String name, Function<PriorityBlockingQueue<QueueItem<?>>, T> function) {
        return function.apply(Constants.QUEUE_MAP.computeIfAbsent(name, this::generateDeque));
    }

    @Scheduled(fixedDelay = 5_000, initialDelay = 15_000)
    @Override
    public void updateMetrics() {
        updateMetrics(meterRegistry, "queue", Constants.QUEUE_MAP);
    }

    @GetMapping("/names")
    public Enumeration<String> names() {
        return Constants.QUEUE_MAP.keys();
    }

    @GetMapping("/all")
    public List<QueueItem<?>> all(@RequestParam("name") String name) {
        return makeReturn(name, ArrayList::new);
    }

    @GetMapping("/size")
    public Integer size(@RequestParam("name") String name) {
        return makeReturn(name, PriorityBlockingQueue::size);
    }

    @GetMapping("/is_empty")
    public Boolean isEmpty(@RequestParam("name") String name) {
        return makeReturn(name, PriorityBlockingQueue::isEmpty);
    }

    @PostMapping("/contains")
    public Boolean contains(@RequestParam("name") String name, @RequestBody QueueItem<?> data) {
        return makeReturn(name, queue -> queue.contains(data));
    }

    @GetMapping("/contains")
    public Boolean containsId(@RequestParam("name") String name, @RequestParam("id") String id) {
        return makeReturn(name, queue -> queue.contains(new QueueItem<>(id)));
    }

    @PostMapping("/add")
    public Boolean add(@RequestParam("name") String name, @RequestBody QueueItem<?> data) {
        return makeReturn(name, queue -> queue.add(data));
    }

    @PostMapping("/remove")
    public Boolean remove(@RequestParam("name") String name, @RequestBody QueueItem<?> data) {
        return makeReturn(name, queue -> queue.remove(data));
    }

    @GetMapping("/remove_id")
    public Boolean removeId(@RequestParam("name") String name, @RequestParam("id") String id) {
        return makeReturn(name, queue -> queue.remove(new QueueItem<>(id)));
    }

    @PostMapping("/contains_all")
    public Boolean containsAll(@RequestParam("name") String name, @RequestBody List<QueueItem<?>> data) {
        return makeReturn(name, queue -> queue.containsAll(data));
    }

    @PostMapping("/contains_all_id")
    public Boolean containsAllId(@RequestParam("name") String name, @RequestBody List<String> ids) {
        List<QueueItem<?>> containsItems = ids.stream().map(QueueItem::new).collect(Collectors.toList());
        return makeReturn(name, queue -> queue.containsAll(containsItems));
    }

    @PostMapping("/add_all")
    public Boolean addAll(@RequestParam("name") String name, @RequestBody List<QueueItem<?>> data) {
        return makeReturn(name, queue -> queue.addAll(data));
    }

    @PostMapping("/remove_all")
    public Boolean removeAll(@RequestParam("name") String name, @RequestBody List<QueueItem<?>> data) {
        return makeReturn(name, queue -> queue.removeAll(data));
    }

    @PostMapping("/remove_all_id")
    public Boolean removeAllId(@RequestParam("name") String name, @RequestBody List<String> ids) {
        return makeReturn(name, queue -> queue.removeIf(item -> ids.contains(item.getId())));
    }

    @GetMapping("/remove_all_id")
    public Boolean removeAllId(@RequestParam("name") String name, @RequestParam("id") String id) {
        return makeReturn(name, queue -> queue.removeIf(item -> item.getId().equals(id)));
    }

    @PostMapping("/retain_all")
    public Boolean retainAll(@RequestParam("name") String name, @RequestBody List<QueueItem<?>> data) {
        return makeReturn(name, queue -> queue.retainAll(data));
    }

    @GetMapping("/clear")
    public void clear(@RequestParam("name") String name) {
        makeVoid(name, PriorityBlockingQueue::clear);
    }

    @PostMapping("/offer")
    public Boolean offer(@RequestParam("name") String name, @RequestBody QueueItem<?> data) {
        return makeReturn(name, queue -> queue.offer(data));
    }

    @GetMapping("/remove")
    public QueueItem<?> remove(@RequestParam("name") String name) {
        return makeReturn(name, PriorityBlockingQueue::remove);
    }

    @GetMapping("/poll")
    public QueueItem<?> poll(@RequestParam("name") String name) {
        return makeReturn(name, queue -> {
            try {
                return queue.poll(Constants.POLL_TIMEOUT, Constants.TIMEOUT_UNIT);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
    }

    @GetMapping("/element")
    public QueueItem<?> element(@RequestParam("name") String name) {
        return makeReturn(name, PriorityBlockingQueue::element);
    }

    @GetMapping("/peek")
    public QueueItem<?> peek(@RequestParam("name") String name) {
        return makeReturn(name, PriorityBlockingQueue::peek);
    }
}
