package br.gov.servicos.editor.cartas;

import br.gov.servicos.editor.oauth2.UserProfile;
import br.gov.servicos.editor.servicos.Metadados;
import br.gov.servicos.editor.servicos.Revisao;
import br.gov.servicos.editor.utils.EscritorDeArquivos;
import br.gov.servicos.editor.utils.LeitorDeArquivos;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;

import static br.gov.servicos.editor.config.CacheConfig.METADADOS;
import static br.gov.servicos.editor.utils.Unchecked.Supplier.uncheckedSupplier;
import static java.lang.String.format;
import static lombok.AccessLevel.PRIVATE;
import static lombok.AccessLevel.PROTECTED;
import static org.eclipse.jgit.lib.Constants.MASTER;
import static org.eclipse.jgit.lib.Constants.R_HEADS;

@FieldDefaults(level = PRIVATE, makeFinal = true)
@CacheConfig(cacheNames = METADADOS, keyGenerator = "geradorDeChavesParaCacheDeCommitsRecentes")
public abstract class ConteudoVersionado<T> {

    @Getter(PROTECTED)
    RepositorioGit repositorio;

    LeitorDeArquivos leitorDeArquivos;
    EscritorDeArquivos escritorDeArquivos;

    public ConteudoVersionado(RepositorioGit repositorio, LeitorDeArquivos leitorDeArquivos, EscritorDeArquivos escritorDeArquivos) {
        this.repositorio = repositorio;
        this.leitorDeArquivos = leitorDeArquivos;
        this.escritorDeArquivos = escritorDeArquivos;
    }

    public abstract String getId();

    public abstract Path getCaminho();

    public abstract T getConteudo();

    protected String getBranchRef() {
        return R_HEADS + getId();
    }

    Path getCaminhoAbsoluto() {
        return repositorio.getCaminhoAbsoluto().resolve(getCaminho()).toAbsolutePath();
    }

    Path getCaminhoRelativo() {
        return repositorio.getCaminhoAbsoluto().relativize(getCaminhoAbsoluto());
    }

    protected Optional<Revisao> getRevisaoMaisRecenteDoArquivo() {
        return repositorio.getRevisaoMaisRecenteDoArquivo(getCaminhoRelativo());
    }

    protected Optional<Revisao> getRevisaoMaisRecenteDoBranch() {
        return repositorio.getRevisaoMaisRecenteDoBranch(getBranchRef());
    }

    @Cacheable
    public Metadados<T> getMetadados() {
        return new Metadados<T>()
                .withId(getId())
                .withPublicado(getRevisaoMaisRecenteDoArquivo().orElse(null))
                .withEditado(getRevisaoMaisRecenteDoBranch().orElse(null))
                .withConteudo(getConteudo());
    }

    @CacheEvict
    public void salvar(UserProfile profile, String conteudo) {
        repositorio.comRepositorioAbertoNoBranch(getBranchRef(), () -> {
            String mensagem = format("%s '%s'", getCaminhoAbsoluto().toFile().exists() ? "Altera" : "Cria", getId());

            repositorio.pull();
            escritorDeArquivos.escrever(getCaminhoAbsoluto(), conteudo);
            repositorio.add(getCaminhoRelativo());
            repositorio.commit(getCaminhoRelativo(), mensagem, profile);
            repositorio.push(getBranchRef());

            return null;
        });
    }

    @CacheEvict
    public void remover(UserProfile profile) {
        repositorio.comRepositorioAbertoNoBranch(this.getBranchRef(), () -> {
            repositorio.pull();

            repositorio.remove(getCaminhoRelativo());
            repositorio.commit(getCaminhoRelativo(), "Remove '" + getId() + "'", profile);
            repositorio.push(getBranchRef());

            return null;
        });
    }

    @CacheEvict
    public void publicar() {
        repositorio.comRepositorioAbertoNoBranch(R_HEADS + MASTER, () -> {
            repositorio.pull();

            repositorio.merge(getBranchRef());
            repositorio.push(R_HEADS + MASTER);

            return null;
        });
    }

    @CacheEvict
    public void renomear(UserProfile profile, String novoNome) {
        repositorio.comRepositorioAbertoNoBranch(getBranchRef(), () -> {
            try {
                String mensagem = format("Renomeia '%s' para '%s'", getId(), novoNome);
                Path novoCaminho = getCaminhoRelativo().resolveSibling(novoNome + ".xml");
                repositorio.moveBranchPara(novoNome);
                Files.move(getCaminhoRelativo(), novoCaminho);
                repositorio.remove(getCaminhoRelativo());
                repositorio.add(novoCaminho);
                repositorio.commit(getCaminhoRelativo(), mensagem, profile);
                repositorio.commit(novoCaminho, mensagem, profile);
                repositorio.push(novoNome);
                return null;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });

    }

    @CacheEvict
    public String getConteudoRaw() throws FileNotFoundException {
        return repositorio.comRepositorioAbertoNoBranch(getBranchRef(), () -> {
            repositorio.pull();
            return leitorDeArquivos.ler(getCaminhoAbsoluto().toFile());
        }).orElseThrow(
                () -> new FileNotFoundException("Não foi possível encontrar o serviço referente ao arquivo '" + getId() + "'")
        );
    }
}

@@ -317,36 +317,8 @@
 er;%0A
-import lombok.SneakyThrows;%0A
 impo
@@ -566,36 +566,8 @@
 on;%0A
-import java.io.IOException;%0A
 impo
@@ -621,36 +621,8 @@
 th;%0A
-import java.nio.file.Paths;%0A
 impo
@@ -4161,38 +4161,34 @@
 (), 
-() -%3E %7B%0A            try %7B%0A    
+uncheckedSupplier(() -%3E %7B%0A
     
@@ -4259,36 +4259,32 @@
 d(), novoNome);%0A
-    
             Path
@@ -4358,36 +4358,32 @@
 %22);%0A            
-    
 repositorio.move
@@ -4396,36 +4396,32 @@
 Para(novoNome);%0A
-    
             File
@@ -4467,36 +4467,32 @@
 o);%0A            
-   
- 
 repositorio.remo
@@ -4521,36 +4521,32 @@
 ));%0A            
-    
 repositorio.add(
@@ -4563,36 +4563,32 @@
 o);%0A            
- 
-   
 repositorio.comm
@@ -4636,36 +4636,32 @@
 e);%0A            
-  
-  
 repositorio.comm
@@ -4700,36 +4700,32 @@
 e);%0A            
-    
 repositorio.push
@@ -4740,36 +4740,32 @@
 e);%0A            
-    
 return null;%0A   
@@ -4773,111 +4773,12 @@
     
- 
-   %7D catch (IOException e) %7B%0A                throw new RuntimeException(e);%0A            %7D%0A        %7D);%0A
+%7D));
 %0A   

Usa uncheckedSupplier - #278