package es.uv.saic.service;

import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;

import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import es.uv.saic.domain.Document;
import es.uv.saic.domain.Email;
import es.uv.saic.domain.EvidenciaTransfer;
import es.uv.saic.domain.Instancia;
import es.uv.saic.domain.InstanciaTasca;
import es.uv.saic.domain.InstanciaTascaRepository;
import es.uv.saic.domain.Plantilla;
import es.uv.saic.domain.TascaVersioTransfer;
import es.uv.saic.domain.Organ;
import es.uv.saic.domain.Usuari;
import es.uv.saic.dto.CategoriaDTO;
import es.uv.saic.dto.InstanciaTascaDTO;
import es.uv.saic.dto.TascaInformeTransferDTO;
import es.uv.saic.feign.PlantillaClient;
import es.uv.saic.feign.DocumentClient;

@Service
public class InstanciaTascaService {

	@Autowired
	private InstanciaTascaRepository instanciaTascaRepository;
	@Autowired
	private InstanciaTascaVerService instanciaTascaVerService;
	@Autowired
	private UsuarisRolService usuarisRolService;
	@Autowired
	private UsuariService usuariService;
	@Autowired
	private InstanciaService instanciaService;
	@Autowired
	private EmailService es;
	@Autowired
	private PlantillaClient plc;
	@Value("${saic.data.filePath}")
	private String filePath;
	
	@Autowired
	private DocumentClient dc;
	
	public List<InstanciaTasca> findByInstancia(BigInteger i_id) {
		return this.instanciaTascaRepository.findByInstancia(i_id);
	}
	
	public InstanciaTasca findByInstanciaTascap(BigInteger i_id, Integer tascap) {
		return this.instanciaTascaRepository.findByInstanciaTascap(i_id, tascap);
	}
	
	public List<TascaVersioTransfer> findOlderVersions(BigInteger idInstancia, Integer idTasca, Integer version) {
		List<InstanciaTasca> itlist = this.instanciaTascaRepository.findOlderVerions(idInstancia, idTasca, version);
		List<TascaVersioTransfer> vtlist = new ArrayList<TascaVersioTransfer>();
		if(itlist != null) {
			if(itlist.size() > 0) {
				for(InstanciaTasca it : itlist) {
					TascaVersioTransfer tvt = new TascaVersioTransfer();
					tvt.setIdInstanciaTasca(it.getIdInstanciaTasca());
					tvt.setTipus(it.getTasca().getTipus().getTipus());
					tvt.setVersion(it.getVersion());
					tvt.setUsuariFet(it.getUsuari() == null ? "" : it.getUsuari().getNom() + " " + it.getUsuari().getCognoms());
					tvt.setDataFet(it.getDataFet());
					tvt.setEstat(it.getEstat());
					if(it.getTasca().getTipus().getTipus() == 11 || it.getTasca().getTipus().getTipus() == 15) {
						Plantilla p = plc.findByID(Integer.parseInt(it.getTasca().getCodiEvidencia()));
						Plantilla p2 = null;
						
						if(it.getInstancia().getTitulacio() > 0) {
							if(!p.getAmbit().equals(it.getInstancia().getOrgan().getTambit())) {
								p2 = plc.findByVersioCodiAmbit(p.getVersio(), p.getCodi(), it.getInstancia().getOrgan().getTambit());
							}
						}			
						if(p2 != null) { p = p2; }
						tvt.setCodiEvidencia(p.getCodi());
						tvt.setNomEvidenciaCas(p.getNomCas());
						tvt.setNomEvidenciaVal(p.getNomVal());
					}
					else {
						tvt.setCodiEvidencia(it.getTasca().getCodiEvidencia());
						tvt.setNomEvidenciaCas(it.getTasca().getNomEvidenciaCas());
						tvt.setNomEvidenciaVal(it.getTasca().getNomEvidenciaVal());
					}
					vtlist.add(tvt);
				}
			}
		}
		return vtlist;
	}
	
	public InstanciaTasca findActiveByInstancia(BigInteger i_id) {
		return this.instanciaTascaRepository.findActiveByInstancia(i_id);
	}
	
	public List<InstanciaTasca> findActivesByType(Integer type) {
		return this.instanciaTascaRepository.findActivesByType(type);
	}
	
	public boolean isUserAuthorized(Usuari u, BigInteger idInstanciaTasca) {
		if(this.instanciaTascaRepository.isUserAutorized(u.getUsuari(), idInstanciaTasca) != null) {
			return true;
		}
		else if(this.usuarisRolService.hasActiveAmbit(u, "C")) {
			return this.instanciaTascaRepository.isUserAutorizedByAmbitC(u.getUsuari(), idInstanciaTasca) != null;
		}
		else {
			if((this.usuarisRolService.hasActiveRol(u, "u_uq") && 
					this.instanciaTascaRepository.getReferenceById(idInstanciaTasca).getTasca().getRol().getNomRol().equals("u_uq"))
				|| (this.usuarisRolService.hasActiveRol(u, "u_admi") && 
						this.instanciaTascaRepository.getReferenceById(idInstanciaTasca).getTasca().getRol().getNomRol().equals("u_admi"))
				|| (this.usuarisRolService.hasActiveRol(u, "adeit") && 
						this.instanciaTascaRepository.getReferenceById(idInstanciaTasca).getTasca().getRol().getNomRol().equals("adeit"))){
				return true;
			}
		}
		
		return false; 
	}
		
	public InstanciaTasca getReportFromProcesOrgan(Integer idProces, String tlugar, Integer lugar,
			 									   Integer centre, Integer titulacio) {
		return this.instanciaTascaRepository.getReportFromProcesOrgan(idProces, tlugar, lugar, centre, titulacio);
	}

	public InstanciaTasca getReportFromNomProcesOrgan(String nomProces, String tlugar, Integer lugar,
			 									      Integer centre, Integer titulacio) {
		return this.instanciaTascaRepository.getReportFromNomProcesOrgan(nomProces, tlugar, lugar, centre, titulacio);
	}
	
	public InstanciaTasca system(InstanciaTasca it) {
		Integer tipus = it.getTasca().getTipus().getTipus();
		Usuari user = this.usuariService.findByUsername("system");
		if(tipus == 13) { /* Convert editable document to PDF */
			Integer idTascap = Integer.parseInt(it.getTasca().getOpcions().replaceAll("[^0-9]", ""));
			InstanciaTasca it2 = this.getNext(it.getInstancia().getIdInstancia(), it.getTasca().getProces().getIdProces(), idTascap);
			String editableFile = this.filePath+it2.getEvidencia();
			String tmpFile = this.filePath+"tmp/"+it2.getIdInstanciaTasca().toString()+".pdf";
			String finalFile = this.filePath+it.getIdInstanciaTasca().toString()+".pdf";
			String ext = StringUtils.getFilenameExtension(editableFile);
			ext = ext == null ? "" : ext;
			
			if(!ext.equals("pdf")) {
				ProcessBuilder builder = new ProcessBuilder();
				builder.command("soffice", "--headless", "--convert-to", "pdf", "--outdir", this.filePath+"tmp", editableFile);
				try {
					Process process = builder.start();
					process.waitFor();
					builder.command("mv", tmpFile, finalFile);
					process = builder.start();
					process.waitFor();
				} 
				catch (IOException e) {
					e.printStackTrace();
				} 
				catch (InterruptedException e) {
					e.printStackTrace();
					if (Thread.interrupted()) {
						Thread.currentThread().interrupt();
					}
				}
			}
			else {
				try {
					ProcessBuilder builder = new ProcessBuilder();
					builder.command("cp", editableFile, finalFile);
					Process process = builder.start();
					process.waitFor();
				} 
				catch (IOException e) {
					e.printStackTrace();
				} 
				catch (InterruptedException e) {
					e.printStackTrace();
					if (Thread.interrupted()) {
						Thread.currentThread().interrupt();
					}
				}
			}
			
			it.setEvidencia(it.getIdInstanciaTasca().toString()+".pdf");
			it.setEstat("E");
			this.saveChanges(user, it);
		}
		else if(tipus == 20) { /* Check if procedure was instanced in the past */
			String [] options = it.getTasca().getOpcions().split(";");
			boolean res = false;
			if(options.length == 2) {
				res = this.instanciaService.exists(options[0], it.getInstancia().getOrgan().getId().getTlugar(), 
						   it.getInstancia().getOrgan().getId().getLugar(), it.getInstancia().getCentre(), 
						   it.getInstancia().getTitulacio(), Integer.valueOf(options[1]));
			}
			else if(options.length == 4) {
				res = this.instanciaService.exists(options[0], it.getInstancia().getOrgan().getId().getTlugar(), 
						   it.getInstancia().getOrgan().getId().getLugar(), it.getInstancia().getCentre(), 
						   it.getInstancia().getTitulacio(), Integer.valueOf(options[1]), Integer.valueOf(options[2]), options[3]);
			}
			else {
				res = false;
			}
				
			if(res) {
				it.setEstat("S");
			}
			else {
				it.setEstat("N");
			}
			this.saveChanges(user, it);
		}
		else if(tipus == 21) { /* Combine PDFs from each idtascap specified in opcions */
			String [] options = it.getTasca().getOpcions().split(";");
			String finalFile = this.filePath+it.getIdInstanciaTasca().toString()+".pdf";
			
			List<String> cmd = new ArrayList<String>();
			List<HashMap<String, String>> evs = new ArrayList<HashMap<String, String>>();
			for(String idtascap : options) {
				InstanciaTasca i = this.findByInstanciaTascap(it.getInstancia().getIdInstancia(), Integer.valueOf(idtascap));
				if(i.getEstat() == null) { /* Task was not submitted */
					continue;
				}
				if(i.getEstat().equals("E")) {
					HashMap<String, String> map = new HashMap<String, String>();
					map.put("codi", i.getTasca().getCodiEvidencia());
					map.put("idtascap", i.getTasca().getIdTascap().toString());
					map.put("evidencia", this.filePath+i.getEvidencia());
					boolean found = false;
					for(HashMap<String, String> m : evs) {
						if(m.get("codi").equals(i.getTasca().getCodiEvidencia())) {
							if(Integer.parseInt(m.get("idtascap")) < i.getTasca().getIdTascap()) { /* Get only the last version */
								m.replace("idtascap", map.get("idtascap"));
								m.replace("evidencia", map.get("evidencia"));
							}
							found = true;
						}
					}
					if(!found) {
						evs.add(map);
					}
				}
			}
			
			cmd.add("pdfunite");
			for(HashMap<String, String> m : evs) {
				cmd.add(m.get("evidencia"));
			}
			cmd.add(finalFile);
			
			ProcessBuilder builder = new ProcessBuilder();
			builder.command(cmd.toArray(new String[]{}));
			try {
				Process process = builder.start();
				process.waitFor();
			}
			catch (IOException e) {
				e.printStackTrace();
			} 
			catch (InterruptedException e) {
				e.printStackTrace();
				if (Thread.interrupted()) {
					Thread.currentThread().interrupt();
				}
			}
			it.setEvidencia(it.getIdInstanciaTasca().toString()+".pdf");
			it.setEstat("E");
			this.saveChanges(user, it);
		}
		else if(tipus == 16){ /* Copy evidence from other procedure */
			String [] options = it.getTasca().getOpcions().split(";");
			if(options.length > 2){
				/* Hay que cambiar esto para usar el id_tascap al seleccionar la evidencia */
				InstanciaTasca itOld = this.getReportFromNomProcesOrgan(options[0], 
																		it.getInstancia().getOrgan().getId().getTlugar(), 
																		it.getInstancia().getOrgan().getId().getLugar(), 
																		it.getInstancia().getCentre(), 
																		it.getInstancia().getTitulacio());

				String copiedPath = this.filePath+itOld.getEvidencia().replace(itOld.getIdInstanciaTasca().toString(), it.getIdInstanciaTasca().toString());
				File copied = new File(copiedPath);
				File original = new File(this.filePath+itOld.getIdInstanciaTasca().toString());

				try {
					FileUtils.copyFile(original, copied);
				} catch (IOException e) {
					e.printStackTrace();
				}

				it.setEvidencia(copied.getName());
			}
			it.setEstat("E");
			this.saveChanges(user, it);
		}
		else if(tipus == 22){ /* Link document from documents as evidence */
			String options = it.getTasca().getOpcions();
			Organ o = it.getInstancia().getOrgan();
			Document d = dc.findByCategoriaOrgan(new CategoriaDTO(Integer.parseInt(options), o.getId().getLugar(), o.getId().getTlugar()));
			it.setEvidencia(d.getRuta());
			it.setEstat("E");
			this.saveChanges(user, it);
		}
		
		return it;
	}
	
	public void saveChanges(Usuari user, InstanciaTasca it) {

		it.setUsuari(user);
		it.setDataFet(new Date(System.currentTimeMillis()));
		this.save(it);
		
		// Set next as active or close instance
		Integer next = 9999;
		if(it.getTasca().getTipus().getTipus() == 2 || it.getTasca().getTipus().getTipus() == 20) {
			if(it.getEstat().equals("S")) {
				next = it.getTasca().getIdTascaSeg();
			}
			else if(it.getEstat().equals("N")){
				next = it.getTasca().getIdTascaSeg2();
			}
		}
		else {
			next = it.getTasca().getIdTascaSeg();
		}
				
		if(next != 9999) {
			InstanciaTasca it_next = this.getNext(it.getInstancia().getIdInstancia(), it.getTasca().getProces().getIdProces(), next);
			if(it_next.getUsuari() == null) {
				it_next.setEstat("A");
				it_next.setData(new Date(System.currentTimeMillis()));
				if(it_next.getTasca().getTipus().getTipus() == 15) {
					InstanciaTasca ta = this.findByInstanciaTascap(it.getInstancia().getIdInstancia(), Integer.parseInt(it_next.getTasca().getOpcions()));
					it_next.setText(ta.getText());
				}
				this.save(it_next);
			}
			else {
				InstanciaTasca it_nextNew = new InstanciaTasca();
				it_nextNew.setDataFet(null);
				it_nextNew.setEstat("A");
				it_nextNew.setData(new Date(System.currentTimeMillis()));
				it_nextNew.setEvidencia(null);
				it_nextNew.setInstancia(it_next.getInstancia());
				it_nextNew.setTasca(it_next.getTasca());
				it_nextNew.setUsuari(null);
				it_nextNew.setVersion(it_next.getVersion()+1);
				it_nextNew.setIdInstanciaTasca(it_next.getIdInstanciaTasca().add(new BigInteger("1")));
				if(it_next.getTasca().getTipus().getTipus() == 15) {
					InstanciaTasca ta = this.findByInstanciaTascap(it.getInstancia().getIdInstancia(), Integer.parseInt(it_next.getTasca().getOpcions()));
					it_nextNew.setText(ta.getText());
				}
				this.save(it_nextNew);
			}
			
			for(Usuari u : this.usuariService.getInstanceAsignedUsers(it.getInstancia().getIdInstancia())) {
				Email email = new Email();
				email.setInstanciaTasca(this.findActiveByInstancia(it.getInstancia().getIdInstancia()));
				email.setUsusari(u);
				es.addEmail(email);
			}
			
			if(this.findActiveByInstancia(it.getInstancia().getIdInstancia()).getTasca().getRol().getNomRol().equals("sys")) {
				this.system(this.findActiveByInstancia(it.getInstancia().getIdInstancia()));
			}
		}
		else {
			Instancia inst = it.getInstancia();
			inst.setEstat("F");
			this.instanciaService.save(inst);
			/* Delete editor drafts from any task associated to this instance */
			this.instanciaTascaVerService.deleteByIdInstancia(inst.getIdInstancia());
		}
	}
	
	public List<EvidenciaTransfer> findOlderByProces(Integer centre, Integer titulacio, String nomProces, Integer minCurs){
		List<EvidenciaTransfer> elist = new ArrayList<EvidenciaTransfer>();
		List<InstanciaTasca> itlist = this.instanciaTascaRepository.findOlderByProces(centre, titulacio, nomProces, minCurs);
		if(itlist != null) {
			if(itlist.size() > 0) {
				for(InstanciaTasca i : itlist) {
					EvidenciaTransfer et = new EvidenciaTransfer();
					et.setData(i.getDataFet());
					et.setEvidencia(i.getEvidencia());
					et.setCurs(i.getTasca().getProces().getCursAvaluat());
					et.setNomProces(i.getTasca().getProces().getNomProces());
					et.setIdTascai(i.getIdInstanciaTasca());
					if(i.getTasca().getTipus().getTipus() == 11 || i.getTasca().getTipus().getTipus() == 15) {
						Plantilla p = plc.findByID(Integer.parseInt(i.getTasca().getCodiEvidencia()));
						Plantilla p2 = null;
						
						if(i.getInstancia().getTitulacio() > 0) {
							if(!p.getAmbit().equals(i.getInstancia().getOrgan().getTambit())) {
								p2 = plc.findByVersioCodiAmbit(p.getVersio(), p.getCodi(), i.getInstancia().getOrgan().getTambit());
							}
						}			
						if(p2 != null) { p = p2; }
								
						et.setCodiEvidencia(p.getCodi());
						et.setNomEvidenciaCas(p.getNomCas());
						et.setNomEvidenciaVal(p.getNomVal());
					}
					else {
						et.setCodiEvidencia(i.getTasca().getCodiEvidencia());
						et.setNomEvidenciaCas(i.getTasca().getNomEvidenciaCas());
						et.setNomEvidenciaVal(i.getTasca().getNomEvidenciaVal());
					}
					elist.add(et);
				}
				return elist;
			}
		}
		
		return null;
	}
	
	public InstanciaTasca findById(BigInteger id) {
		Optional<InstanciaTasca> i = this.instanciaTascaRepository.findById(id);
		if(i.isEmpty()) {
			return null;
		}
		else {
			return i.get();
		}
	}

	public InstanciaTascaDTO findByIdDTO(BigInteger id) {
		Optional<InstanciaTasca> i = this.instanciaTascaRepository.findById(id);
		
		if(i.isEmpty()) {
			return null;
		}
		else {
			return new InstanciaTascaDTO(i.get());
		}
	}
	
	public List<InstanciaTasca> getPastTasks(){
		return this.instanciaTascaRepository.getPastTasks();
	}
		
	public InstanciaTasca getNext(BigInteger idInstancia, Integer idProces, Integer idTascap) {
		return this.instanciaTascaRepository.findNext(idInstancia, idProces, idTascap);
	}
	
	public List<Usuari> getUsers(BigInteger idInstancia){
		return this.instanciaTascaRepository.getUsers(idInstancia);
	}
	
	public TascaInformeTransferDTO getLastByProcName(String nomProces, Integer titulacio, Integer centre, String ambit) {
		return this.instanciaTascaRepository.getLastByProcName(nomProces, titulacio, centre, ambit);
	}
	
	public void deactivateAll(BigInteger idInstancia) {
		this.instanciaTascaRepository.deactivateAll(idInstancia);
	}
	
	public void remove(BigInteger idInstanciaTasca) {
		this.instanciaTascaRepository.remove(idInstanciaTasca);
	}
	
	public void removeAllVersions(BigInteger idInstancia, Integer idTasca) {
		this.instanciaTascaRepository.removeAllVersions(idInstancia, idTasca);
	}
	
	public void save(InstanciaTasca i) {
		this.instanciaTascaRepository.save(i);
		this.instanciaTascaRepository.flush();
	}
}
