package br.gov.servicos.servico;

import br.gov.servicos.foundation.exceptions.ConteudoNaoEncontrado;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Stream;

import static java.util.Optional.ofNullable;
import static java.util.stream.Collectors.toList;
import static java.util.stream.StreamSupport.stream;
import static lombok.AccessLevel.PRIVATE;
import static org.springframework.data.domain.Sort.Direction.ASC;
import static org.springframework.web.bind.annotation.RequestMethod.GET;

@Controller
@FieldDefaults(level = PRIVATE, makeFinal = true)
class ServicoController {

    ServicoRepository servicos;

    @Autowired
    ServicoController(ServicoRepository servicos) {
        this.servicos = servicos;
    }

    @RequestMapping(value = "/servicos", method = GET)
    ModelAndView todos(@RequestParam(required = false) Character letra) {
        Character primeiraLetra = ofNullable(letra).orElse('A');

        Map<String, Object> model = new HashMap<>();
        model.put("servicos", servicosOrdenadosPorTitulo(primeiraLetra));
        model.put("letras", iniciaisDosServicos());
        model.put("letraAtiva", primeiraLetra);

        return new ModelAndView("servicos", model);
    }

    @RequestMapping(value = "/servico/{id}", method = GET)
    ModelAndView get(@PathVariable("id") String id) {
        Servico servico = servicos.save(buscaServico(id).withNovoAcesso());
        return new ModelAndView("servico", "servico", servico);
    }

    @RequestMapping(value = "/navegar/{id}", method = GET)
    RedirectView navegar(@PathVariable("id") String id) {
        return navegaPara(id, Servico::getUrl);
    }

    @RequestMapping(value = "/agendar/{id}", method = GET)
    public RedirectView agendar(@PathVariable("id") String id) {
        return navegaPara(id, Servico::getUrlAgendamento);
    }

    private RedirectView navegaPara(String id, Function<Servico, String> url) {
        Servico servico = servicos.save(buscaServico(id).withNovaAtivacao());

        return ofNullable(servico)
                .map(url)
                .map(RedirectView::new)
                .orElseThrow(ConteudoNaoEncontrado::new);
    }

    @SneakyThrows
    private Servico buscaServico(String id) {
        Servico servico = servicos.findOne(id);
        return ofNullable(servico).orElseThrow(ConteudoNaoEncontrado::new);
    }

    private Iterable<Servico> servicosOrdenadosPorTitulo(Character primeiraLetra) {
        return servicosOrdenadosPorTitulo()
                .filter(servico -> primeiraLetra.equals(primeiraLetraDoTitulo(servico)))
                .collect(toList());
    }

    private Iterable<Character> iniciaisDosServicos() {
        return servicosOrdenadosPorTitulo()
                .map(this::primeiraLetraDoTitulo)
                .distinct()
                .sorted()
                .collect(toList());
    }

    private Stream<Servico> servicosOrdenadosPorTitulo() {
        return stream(servicos.findAll(new Sort(ASC, "titulo")).spliterator(), false);
    }

    private char primeiraLetraDoTitulo(Servico servico) {
        return servico.getTitulo().toUpperCase().charAt(0);
    }
}

@@ -642,24 +642,47 @@
 il.HashMap;%0A
+import java.util.List;%0A
 import java.
@@ -730,40 +730,8 @@
 ion;
-%0Aimport java.util.stream.Stream;
 %0A%0Aim
@@ -769,24 +769,78 @@
 ofNullable;%0A
+import static java.util.stream.Collectors.groupingBy;%0A
 import stati
@@ -1527,18 +1527,139 @@
 ra).
-orElse('A'
+map(Character::toUpperCase).orElse('A');%0A        Map%3CCharacter, List%3CServico%3E%3E servicosPorLetra = servicosAgrupadosPorLetraInicial(
 );%0A%0A
@@ -1711,16 +1711,64 @@
 ap%3C%3E();%0A
+        model.put(%22letraAtiva%22, primeiraLetra);%0A
         
@@ -1797,34 +1797,28 @@
 servicos
-OrdenadosPorTitulo
+PorLetra.get
 (primeir
@@ -1858,78 +1858,186 @@
 as%22,
- iniciaisDosServicos());%0A        model.put(%22letraAtiva%22, primeiraLetra
+%0A                servicosPorLetra%0A                        .keySet()%0A                        .stream()%0A                        .sorted()%0A                        .collect(toList())
 );%0A%0A
@@ -3253,557 +3253,70 @@
 ate 
-Iterable%3CServico%3E servicosOrdenadosPorTitulo(Character primeiraLetra) %7B%0A        return servicosOrdenadosPorTitulo()%0A                .filter(servico -%3E primeiraLetra.equals(primeiraLetraDoTitulo(servico)))%0A                .collect(toList());%0A    %7D%0A%0A    private Iterable%3CCharacter%3E iniciaisDosServicos() %7B%0A        return servicosOrdenadosPorTitulo()%0A                .map(this::primeiraLetraDoTitulo)%0A                .distinct()%0A                .sorted()%0A                .collect(toList());%0A    %7D%0A%0A    private Stream%3CServico%3E servicosOrdenadosPorTitulo
+Map%3CCharacter, List%3CServico%3E%3E servicosAgrupadosPorLetraInicial
 () %7B
@@ -3405,97 +3405,51 @@
 lse)
-;
 %0A    
-%7D%0A%0A
     
-private char primeiraLetraDoTitulo(Servico servico) %7B%0A        return servico
+        .collect(groupingBy(s -%3E s
 .get
@@ -3480,18 +3480,20 @@
 harAt(0)
+))
 ;%0A    %7D%0A
 %7D%0A
@@ -3490,10 +3490,11 @@
 ;%0A    %7D%0A
+%0A
 %7D%0A

Simplifica agrupamento de servicos por letra inicial - #107