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.ConcurrentLinkedDeque;
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("deque")
public class DequeController extends QueueOperator {
    private static final Logger logger = LoggerFactory.getLogger(DequeController.class);
    private final MeterRegistry meterRegistry;

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

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

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

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

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

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

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

    @PostMapping("/add_first")
    public void addFirst(@RequestParam("name") String name, @RequestBody QueueItem<?> data) {
        makeVoid(name, deque -> deque.addFirst(data));
    }

    @PostMapping("/add_last")
    public void addLast(@RequestParam("name") String name, @RequestBody QueueItem<?> data) {
        makeVoid(name, deque -> deque.addLast(data));
    }

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

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

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

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

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

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

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

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

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

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

    @PostMapping("/remove_first_occurrence")
    public Boolean removeFirstOccurrence(@RequestParam("name") String name, @RequestBody String data) {
        return makeReturn(name, deque -> deque.removeFirstOccurrence(data));
    }

    @GetMapping("/remove_first_occurrence_id")
    public Boolean removeFirstOccurrenceId(@RequestParam("name") String name, @RequestParam("id") String id) {
        return makeReturn(name, deque -> deque.removeFirstOccurrence(new QueueItem<>(id)));
    }

    @PostMapping("/remove_last_occurrence")
    public Boolean removeLastOccurrence(@RequestParam("name") String name, @RequestBody String data) {
        return makeReturn(name, deque -> deque.removeLastOccurrence(data));
    }

    @GetMapping("/remove_last_occurrence_id")
    public Boolean removeLastOccurrenceId(@RequestParam("name") String name, @RequestParam("id") String id) {
        return makeReturn(name, deque -> deque.removeLastOccurrence(new QueueItem<>(id)));
    }

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

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

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

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

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

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

    @PostMapping("/push")
    public void push(@RequestParam("name") String name, @RequestBody QueueItem<?> data) {
        makeVoid(name, deque -> deque.push(data));
    }

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

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

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

    @PostMapping("/contains_all")
    public Boolean containsAll(@RequestParam("name") String name, @RequestBody List<QueueItem<?>> data) {
        return makeReturn(name, deque -> deque.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, deque -> deque.containsAll(containsItems));
    }

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

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

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

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

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

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

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

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

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

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