package br.mp.mprj.portal.portlet.gestaomensagens.service.service.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import javax.portlet.PortletRequest;
import javax.portlet.PortletSession;

import org.apache.commons.lang.StringUtils;

import br.mp.mprj.portal.portlet.gestaomensagens.enums.SituacaoEnvioEnum;
import br.mp.mprj.portal.portlet.gestaomensagens.enums.TipoAgendamentoEnum;
import br.mp.mprj.portal.portlet.gestaomensagens.enums.TipoEnvioEnum;
import br.mp.mprj.portal.portlet.gestaomensagens.service.NoSuchMensagemException;
import br.mp.mprj.portal.portlet.gestaomensagens.service.model.Agendamento;
import br.mp.mprj.portal.portlet.gestaomensagens.service.model.AnexoMensagem;
import br.mp.mprj.portal.portlet.gestaomensagens.service.model.Mensagem;
import br.mp.mprj.portal.portlet.gestaomensagens.service.service.AgendamentoLocalServiceUtil;
import br.mp.mprj.portal.portlet.gestaomensagens.service.service.MensagemLocalServiceUtil;
import br.mp.mprj.portal.portlet.gestaomensagens.service.service.TemplateLocalServiceUtil;
import br.mp.mprj.portal.portlet.gestaomensagens.service.service.base.MensagemLocalServiceBaseImpl;
import br.mp.mprj.portal.portlet.gestaomensagens.util.EmailUtil;
import br.mp.mprj.portal.portlet.gestaomensagens.util.GestaoMensagensUtil;
import br.mp.mprj.portal.portlet.gestaomensagens.workflow.StatusWorkflowEnum;

import com.liferay.counter.service.CounterLocalServiceUtil;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.search.Indexer;
import com.liferay.portal.kernel.search.IndexerRegistryUtil;
import com.liferay.portal.kernel.upload.UploadPortletRequest;
import com.liferay.portal.kernel.util.ArrayUtil;
import com.liferay.portal.kernel.util.CharPool;
import com.liferay.portal.kernel.util.ContentTypes;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.ParamUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.kernel.util.WebKeys;
import com.liferay.portal.kernel.workflow.WorkflowConstants;
import com.liferay.portal.theme.ThemeDisplay;
import com.liferay.util.portlet.PortletProps;

/**
 * 
 * @author HenriqueSimoes
 *
 */
public class MensagemLocalServiceImpl extends MensagemLocalServiceBaseImpl {

	private static final String ATRIBUTO_TAMANHO_MINIMO = "mensagem.protocolo.tamanho-minimo";
	
	/**
	 * String de placeholder para ser substitu&iacute;do pelo valor rerentes ao ano e m&ecirc;s da data atual (servidor).
	 */
	private static final String PLACEHOLDER_ANOMES = "[ANOMES]";
	
	/**
	 * Nome do counter para ser utilizado atrav&eacute;s do {@link CounterLocalServiceUtil}. <br>
	 * Utilizando o caracter especial {@link CharPool#POUND} (#) para definir que a prioridade de sequencial do pool deve ser o que 
	 * foi definido no arquivo portal-ext.properties, para utilizar qualquer nome que inicie com o namespace do protocolo até o #,
	 * abrangendo assim qualquer AnoMes sem precisar configurá-los individualmente no arquivo de propriedades, ex: <br>
	 *  gestaomensagens.protocolo#2016010001<br>
	 *  gestaomensagens.protocolo#2016020002<br>
	 *  ...<br>
	 *  gestaomensagens.protocolo#2055010001<br>
	 *  ...
	 */
	private static final String NAMESPACE_PROTOCOLO_COUNTER = "gestaomensagens.protocolo" + CharPool.POUND + PLACEHOLDER_ANOMES;
	
	/**
	 * String a ser utilizado padding na formatação do numero do protocolo.
	 */
	private static final String PADCHAR = "0";
	
	/**
	 * Retorna a mensagem de rascunho do usuario criador informado.
	 * @param userIdCriador
	 * @return {@link Mensagem}
	 * @throws PortalException
	 * @throws SystemException
	 */
	public Mensagem getRascunho(long userIdCriador)
			throws PortalException, SystemException {
		
		if (mensagemPersistence.fetchByRascunhoCriador(true, userIdCriador) != null){
			return mensagemPersistence.findByRascunhoCriador(true, userIdCriador);
		} else {
			throw new NoSuchMensagemException("Nao existem rascunhos para o usuario criado -> " + userIdCriador);
		}
	}
	
	@Override
	public Mensagem updateMensagem(Mensagem mensagem) throws SystemException {
		try {
			
			//gerar o numero do protocolo, se necessario.
			if (!mensagem.isRascunho() 
					&& Validator.isNull(mensagem.getCodigoMensagem())) {
				mensagem.setCodigoMensagem(gerarProtocolo());
			}

			mensagem = super.updateMensagem(mensagem);
			
//			System.out.println(mensagem.getMensagemId() 
//						+ ", status workflow: " 
//						+ mensagem.getStatusWorkflow() 
//						+ ", situacao envio: " + mensagem.getSituacaoEnvio());
			
			gravarAgendamento(mensagem);
			
			//forcar que no proximo update, nao seja feito agendamento,
			//a nao ser que a funcionalidade que original a chamada ao servico
			//sobrescreva definindo como true novamente.
			mensagem.setGravarAgendamento(false);
			
			try {
				gravarAssetEntry(mensagem);
			}catch (Exception ex) {
				return mensagem;
			}
			
		}
		catch (Exception ex) {
			throw new SystemException(ex);
		}
		return mensagem;
	}
	
	private void gravarAssetEntry(Mensagem mensagem)
			throws PortalException, SystemException {		
		assetEntryLocalService.updateEntry(mensagem.getUserIdCriador(), mensagem.getGroupId(), 
				mensagem.getDataSolicitacao(), mensagem.getDataAlteracao(), Mensagem.class.getName(), 
				mensagem.getMensagemId(), mensagem.getUuid(), 0, 
				null, null, 
				false, null, null, null, ContentTypes.TEXT_HTML, 
				mensagem.getAssunto(), mensagem.getAssunto(), mensagem.getConteudo(), 
				null, null, 0, 0, null, false);
	}
	
	private void gravarAgendamento(Mensagem mensagem) throws PortalException, SystemException {
		if (!mensagem.isGravarAgendamento()) return;
		
		//limpar o calendario
		List<Long> listIdNaoExcluir = new ArrayList<>();
		
		for (Agendamento ag : mensagem.getAgendamentos()) {
			agendamentoLocalService.updateAgendamento(ag);
			
			//processar apenas tipo calendario
			if (ag.getTipoAgendamento() == TipoAgendamentoEnum.CALENDARIO.ordinal() 
					&& ag.isNaoExcluir()) {
				listIdNaoExcluir.add(ag.getAgendamentoId());
			}
			
			ag.setNaoExcluir(false);
		}
		
		int[] tiposExclusao = {TipoAgendamentoEnum.CALENDARIO.ordinal(),
								TipoAgendamentoEnum.IMEDIATO.ordinal()};
		
		excluirAgendamentos(mensagem, listIdNaoExcluir, tiposExclusao);
		
		if (mensagem.getTipoEnvio() == TipoEnvioEnum.IMEDIATO.ordinal()) {
			//adiciona um agendamento referente ao horario atual
			Agendamento ag = agendamentoLocalService.createAgendamento(CounterLocalServiceUtil.increment());
			ag.setCompanyId(mensagem.getCompanyId());
			ag.setGroupId(mensagem.getGroupId());
			ag.setMensagemId(mensagem.getMensagemId());
			ag.setDataInicio(new Date());
			ag.setDataAtualizacao(ag.getDataInicio());
			ag.setTipoAgendamento(TipoAgendamentoEnum.IMEDIATO.ordinal());
			agendamentoLocalService.updateAgendamento(ag);
		}
		
		agendamentoLocalService.carregarAgendamento(mensagem, true);
	}
	
	private void excluirAgendamentos(Mensagem mensagem, List<Long> listIdNaoExcluir, int[] tiposExclusao)
			throws PortalException, SystemException {
		//obter as referencias dos agendamentos que devem ser excluidos, 
		//desconsiderando os ID's que foram marcados para nao serem excluidos
		List<Agendamento> listAgExc = agendamentoPersistence.findByAgendamentoExclusao(mensagem.getMensagemId(), 
																tiposExclusao, 
																SituacaoEnvioEnum.PENDENTE.ordinal(), 
																ArrayUtil.toArray(listIdNaoExcluir.toArray(new Long[listIdNaoExcluir.size()]))); 
		for (Agendamento agExc : listAgExc) {
			agendamentoLocalService.deleteAgendamento(agExc);
		}
	}
	
	

	/**
	 * Tenta recuperar a mensagem de rascunho, caso nao exista, tenta recuperar a mensagem da sessao.
	 */
	public Mensagem getMensagem(UploadPortletRequest uploadReq , PortletRequest req)
			throws PortalException, SystemException {
		
		PortletSession session = req.getPortletSession();
		
		ThemeDisplay themeDisplay = (ThemeDisplay) req.getAttribute(WebKeys.THEME_DISPLAY);

		long mensagemId = ParamUtil.getLong(uploadReq, "mensagemId");
		boolean recuperouSession = false;

		Mensagem mensagem = null;
		
		//verifica primeiro se existe mensagemId no parametro, onde tera prioridade neste caso.
		if (mensagemId > 0) {
			mensagem = MensagemLocalServiceUtil.getMensagem(mensagemId);
			session.removeAttribute("rascunhoRecuperado");
		}
		else {
			if(session.getAttribute("mensagem") != null){
				mensagem = (Mensagem)session.getAttribute("mensagem");
				session.setAttribute("rascunhoRecuperado", true);
			}
			recuperouSession = (mensagem != null);
		}
		
		try {
			//tentar obter a mensagem de rascunho no banco de dados para o usuario atual (criador)
			if (mensagem == null) {
				if (isRascunho(req)) {
					mensagem = MensagemLocalServiceUtil.getRascunho(themeDisplay.getUserId());	
					session.setAttribute("rascunhoRecuperado", true);
				}
			}
			
			//carregar agendamento apenas se encontrou mensagem,
			//e a mesma nao foi recuperada da session 
			//(evitar consulta desnecessaria ao servico)
			if (mensagem != null 
					&& !recuperouSession) {
				agendamentoLocalService.carregarAgendamento(mensagem, true);
			}			
			
			//define a referencia ao template (transient) caso necessario.
			if (mensagem != null 
					&& mensagem.getTemplate() == null
					&& mensagem.getTemplateId() > 0) {
				mensagem.setTemplate(TemplateLocalServiceUtil.getTemplate(mensagem.getTemplateId()));
			}
			
			if (mensagem == null){
				mensagem = createMensagem(CounterLocalServiceUtil.increment());
				inicializarDadosMensagem(req, mensagem);
			}
		}
		catch (NoSuchMensagemException nex) {
			mensagem = createMensagem(CounterLocalServiceUtil.increment());
			inicializarDadosMensagem(req, mensagem);
		}
		
		session.setAttribute("mensagem", mensagem);
		
		return mensagem;
	}
	
	
	/**
	 * Tenta recuperar a mensagem de rascunho, caso nao exista, tenta recuperar a mensagem da sessao.
	 */
	public Mensagem getMensagem(PortletRequest req)
			throws PortalException, SystemException {
		PortletSession session = req.getPortletSession();
		
		ThemeDisplay themeDisplay = (ThemeDisplay) req.getAttribute(WebKeys.THEME_DISPLAY);

		long mensagemId = ParamUtil.getLong(req, "mensagemId");
		boolean recuperouSession = false;

		Mensagem mensagem = null;
		
		//verifica primeiro se existe mensagemId no parametro, onde tera prioridade neste caso.
		if (mensagemId > 0) {
			mensagem = MensagemLocalServiceUtil.getMensagem(mensagemId);
			session.removeAttribute("rascunhoRecuperado");
		}
		else {
			if(session.getAttribute("mensagem") != null){
				mensagem = (Mensagem)session.getAttribute("mensagem");
				session.setAttribute("rascunhoRecuperado", true);
			}
			recuperouSession = (mensagem != null);
		}
		
		try {
			//tentar obter a mensagem de rascunho no banco de dados para o usuario atual (criador)
			if (mensagem == null) {
				if (isRascunho(req)) {
					mensagem = MensagemLocalServiceUtil.getRascunho(themeDisplay.getUserId());	
					session.setAttribute("rascunhoRecuperado", true);
				}
			}
			
			//carregar agendamento apenas se encontrou mensagem,
			//e a mesma nao foi recuperada da session 
			//(evitar consulta desnecessaria ao servico)
			if (mensagem != null 
					&& !recuperouSession) {
				agendamentoLocalService.carregarAgendamento(mensagem, true);
			}			
			
			//define a referencia ao template (transient) caso necessario.
			if (mensagem != null 
					&& mensagem.getTemplate() == null
					&& mensagem.getTemplateId() > 0) {
				mensagem.setTemplate(TemplateLocalServiceUtil.getTemplate(mensagem.getTemplateId()));
			}
			
			if (mensagem == null){
				mensagem = createMensagem(CounterLocalServiceUtil.increment());
				inicializarDadosMensagem(req, mensagem);
			}
		}
		catch (NoSuchMensagemException nex) {
			mensagem = createMensagem(CounterLocalServiceUtil.increment());
			inicializarDadosMensagem(req, mensagem);
		}
		
		session.setAttribute("mensagem", mensagem);
		
		return mensagem;
	}
	
	/**
	 * Define os dados iniciais da mensagem.
	 */
	private void inicializarDadosMensagem(PortletRequest req, Mensagem mensagem) {
		ThemeDisplay themeDisplay = (ThemeDisplay) req.getAttribute(WebKeys.THEME_DISPLAY);

		mensagem.setUserIdCriador(themeDisplay.getUserId());
		mensagem.setGroupId(themeDisplay.getScopeGroupId());
		mensagem.setCompanyId(themeDisplay.getCompanyId());
		mensagem.setRascunho(true);
		mensagem.setSituacaoEnvio(-1);
		mensagem.setDataSolicitacao(new Date());
		mensagem.setAtivo(true);
		mensagem.setNomeSolicitante(themeDisplay.getUser().getFullName());
		mensagem.setStatusWorkflow(WorkflowConstants.STATUS_DRAFT);
		
	}
	
	
	
	/**
	 * Retorna se est&aacute; no modo wizard rascunho, ou se est&aacute; 
	 * editando alguma mensagem previamente gravada (geralmente voltando ao fluxo de aprova&ccedil;&atilde;o).
	 */
	public boolean isRascunho(PortletRequest req) {
		return ParamUtil.getBoolean(req, "rascunho");
	}
	
	@Override
	public Mensagem deleteMensagem(long mensagemId) throws PortalException,
			SystemException {
		Mensagem mensagem = getMensagem(mensagemId);
		
		agendamentoPersistence.removeByMensagemId(mensagemId);
		
		deleteAssetEntry(mensagem);
		
		workflowInstanceLinkLocalService.deleteWorkflowInstanceLink(
				mensagem.getCompanyId(), mensagem.getGroupId(),
				Mensagem.class.getName(), mensagem.getMensagemId());
		
		mensagem = super.deleteMensagem(mensagemId);
		
		Indexer indexer = IndexerRegistryUtil.nullSafeGetIndexer(Mensagem.class);
		indexer.delete(mensagem);
		
		return mensagem;
	}
	
	@Override
	public Mensagem deleteMensagem(Mensagem mensagem) throws SystemException {
		try {
			return deleteMensagem(mensagem.getMensagemId());
		} 
		catch (PortalException e) {
			throw new SystemException(e);
		}
	}
	
	private void deleteAssetEntry(Mensagem mensagem) throws SystemException, PortalException {
		assetEntryLocalService.deleteEntry(Mensagem.class.getName(), mensagem.getMensagemId());		
	}
	
	/**
	 * Gera o n&uacute;mero do protocolo e o retorna no formato ANOSEQUENCIAL, ex: 20160001. <br>
	 * Reiniciando a numera&ccedil;&atilde;o a cada m&ecirc;s.
	 * @return String
	 * @throws PortalException
	 * @throws SystemException
	 */
	private String gerarProtocolo() throws PortalException, SystemException {
		Calendar dataAtual = GregorianCalendar.getInstance();
		
		int ano = dataAtual.get(Calendar.YEAR);
		
		//formata o numero do mes com zero a esquerda quando necessario.
		String anoMes = ano + StringUtils.leftPad(String.valueOf(dataAtual.get(Calendar.MONTH) + 1), 2, PADCHAR);
		
		long sequencial = CounterLocalServiceUtil.increment(NAMESPACE_PROTOCOLO_COUNTER.replace(PLACEHOLDER_ANOMES, anoMes), 1);
		return anoMes +
					StringUtils.leftPad(String.valueOf(sequencial), 
										GetterUtil.getInteger(PortletProps.get(ATRIBUTO_TAMANHO_MINIMO)), 
										PADCHAR);
	}	
	
	/**
	 * Metodo responsavel pelo cancelamento de todos os seus agendamentos pendentes da mensagem.
	 */
	public void cancelarMensagem(long mensagemId) throws PortalException, SystemException{
		
		List<Agendamento> agendamentos = agendamentoLocalService.recuperaAgendamentosPorSituacao(mensagemId, SituacaoEnvioEnum.PENDENTE.ordinal());
		
		for(Agendamento ag : agendamentos){
			ag.setSituacaoEnvio(SituacaoEnvioEnum.CANCELADO.ordinal());
			agendamentoLocalService.updateAgendamento(ag);
		}
		
	}
	
	/**
	 * Metodo responsavel pela clonagem de uma mensagem existente 
	 * e todos os seus agendamentos pendentes.
	 */
	public Mensagem clonaMensagem(long mensagemId, PortletRequest req) throws PortalException, SystemException{
		
		Mensagem mensagem = getMensagem(mensagemId);
		final List<Agendamento> agendamentosClonados = new ArrayList<Agendamento>(); 
		
		Mensagem mensagemClonada = createMensagem(CounterLocalServiceUtil.increment());
		inicializarDadosMensagem(req, mensagemClonada);
		
		mensagemClonada.setAgendamentos(mensagem.getAgendamentos());
		mensagemClonada.setAssunto(mensagem.getAssunto());
		mensagemClonada.setContaSaidaId(mensagem.getContaSaidaId());
		mensagemClonada.setConteudo(mensagem.getConteudo());
		mensagemClonada.setCodigoMensagem(gerarProtocolo());
		mensagemClonada.setGrupoDestinatarioIds(mensagem.getGrupoDestinatarioIds());
		mensagemClonada.setTemplateId(mensagem.getTemplateId());
		mensagemClonada.setTemplate(mensagem.getTemplate());
		mensagemClonada.setTipoEnvio(mensagem.getTipoEnvio());
		mensagemClonada.setDatasCalendario(mensagem.getDatasCalendario());
		mensagemClonada.setClonagemPai(mensagem.getMensagemId());
		mensagemClonada.setPrioridade(mensagem.getPrioridade());
		
		updateMensagem(mensagemClonada);
		
	//	List<Agendamento> agendamentos = agendamentoLocalService.recuperaAgendamentosPorSituacao(mensagemId, SituacaoEnvioEnum.PENDENTE.ordinal());
		
		List<Agendamento> agendamentos = agendamentoLocalService.recuperaAgendamentos(mensagemId);
		List<AnexoMensagem> anexos = anexoMensagemLocalService.recuperaAnexosPorMensagem(mensagemId);
		
		for(Agendamento ag : agendamentos){
			
			Agendamento agendamento = AgendamentoLocalServiceUtil.createAgendamento(CounterLocalServiceUtil.increment());
			agendamento.setCompanyId(mensagem.getCompanyId());
			agendamento.setGroupId(mensagem.getGroupId());
			agendamento.setMensagemId(mensagem.getMensagemId());
			agendamento.setTipoAgendamento(TipoAgendamentoEnum.CALENDARIO.ordinal());
			agendamento.setMensagemId(mensagemClonada.getMensagemId());
			
			agendamento.setDataCalendario(ag.getDataCalendario());
			agendamento.setDataInicio(ag.getDataInicio());
			agendamento.setDataFim(ag.getDataFim());
			agendamento.setTipoAgendamento(ag.getTipoAgendamento());
			
			AgendamentoLocalServiceUtil.updateAgendamento(agendamento);
			
			agendamentosClonados.add(agendamento);
			
			agendamentoLocalService.updateAgendamento(agendamento);
		}
		
		if (agendamentosClonados.size() > 0){
			agendamentoLocalService.carregarAgendamento(mensagemClonada, true);
		}
		
		//Ponteirando os arquivos anexos associados para a mensagem clonada.
		if (anexos != null && anexos.size() > 0){
			for(AnexoMensagem anexo : anexos){
				anexo.setMensagemId(mensagemClonada.getMensagemId());
			}
			anexoMensagemLocalService.atualizaAnexos(anexos);
		}
		
		return mensagemClonada;
	}
}
