package com.bytesw.bytebot.service;

import com.bytesw.bytebot.bean.*;
import com.bytesw.bytebot.etl.batch.service.ScheduleService;
import com.bytesw.bytebot.model.*;
import com.bytesw.bytebot.jdbc.AgentJDBCRepository;
import com.bytesw.bytebot.model.enums.*;
import com.bytesw.bytebot.repository.*;
import com.bytesw.xdf.config.AppContextManager;
import com.bytesw.xdf.exception.NotFoundException;
import com.bytesw.xdf.sql.beans.Pagination;
import lombok.extern.log4j.Log4j2;
import org.apache.camel.ProducerTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Service
@Transactional
@Log4j2
public class AgentService extends CustomPaginationService<Agent> {

    @Autowired
    private AgentRepository agentRepository;

    @Autowired
    private AgentJDBCRepository agentJDBCRepository;

    @Autowired
    private CountryRepository countryRepository;

    @Autowired
    private DeploymentChannelRepository deploymentChannelRepository;

    @Autowired
    private DeploymentChannelParamValueRepository deploymentChannelParamValueRepository;

    @Autowired
    private ChannelRepository channelRepository;

    @Autowired
    private ChannelParamRepository channelParamRepository;

    @Autowired
    private FrequentQuestionRepository frequentQuestionRepository;

    @Autowired
    private QuestionFileRepository questionFileRepository;

    @Autowired
    private ProducerTemplate producerTemplate;

    @Autowired
    private ScheduleService scheduleService;

    public List<DeploymentChannelParamValueBean> GetKeysFromAgents(AgentStatusEnum status){
        List<DeploymentChannelParamValueBean> values = agentJDBCRepository.getCredentialsFromAgentStatus(status.getName());

        if (values == null){
            values = new ArrayList<>();
        }
        //log.debug(values);
        return values;
    }

    public void searchByPagination(Pagination<AgentBean> pagination) {
        if (pagination.getItemsPerPage() == 0) {
            pagination.setItemsPerPage(10);
        }

        Sort sort = null;
        if (pagination.getSortFields() != null) {
            sort = createSort(pagination);
        }

        Pageable pageable = null;
        if (sort != null) {
            pageable = PageRequest.of(pagination.getCurrentPage(), pagination.getItemsPerPage(), sort);
        } else {
            pageable = PageRequest.of(pagination.getCurrentPage(), pagination.getItemsPerPage());
        }

        Specification<Agent> specification = (Specification<Agent>) (root, query, criteriaBuilder)
                -> criteriaBuilder.notEqual(root.get("status"), AgentStatusEnum.DELETED);

        if (pagination.getFilterExpression() != null && pagination.getFilterExpression().length() > 0) {
            specification = createSpecification(pagination.getFilterExpression()).and(specification);
        }

        Page<Agent> page = agentRepository.findAll(specification, pageable);

        pagination.setTotalItems(page.getTotalElements());
        pagination.setTotalPages(page.getTotalPages());

        // Clone to array
        List<AgentBean> list = new ArrayList<>();

        for (Agent agent : page.getContent()) {
            list.add(AgentBean.miniClone(agent));
        }

        pagination.setData(list);
    }

    @Transactional(propagation = Propagation.REQUIRED)
    @CacheEvict(value="deploy_agents",allEntries = true)
    public void save(AgentBean agent) {

        boolean isModify = true;

        Agent agentBD = null;

        if (agent.getId() != null) {
            Optional<Agent> agentFound = agentRepository.findById(agent.getId());

            if (agentFound.isPresent()) {
                agentBD = agentFound.get();
            }
        }

        if (agentBD == null) {
            agentBD = new Agent();
            agentBD.setStatus(AgentStatusEnum.CREATED);

            isModify = false;
        } else {
            agentBD.setStatus(AgentStatusEnum.fromString(agent.getStatus()));
        }

        // General Information

        agentBD.setName(agent.getName());
        agentBD.setDescription(agent.getDescription());
        agentBD.setLanguage(LanguageEnum.fromString(agent.getLanguage()));
        agentBD.setTimezone(agent.getTimezone());
        agentBD.setVersion(agent.getVersion());
        agentBD.setType(AgentTypeEnum.fromString(agent.getType()));
        agentBD.setAvatar(agent.getAvatar());
        agentBD.setPeriod(agent.getPeriod());

        if (agent.getCountryId() != null) {
            Optional<Country> countryFound = countryRepository.findById(agent.getCountryId());
            if (countryFound.isPresent()) {
                agentBD.setCountry(countryFound.get());
            }
        } else {
            agentBD.setCountry(null);
        }

        agentBD = agentRepository.save(agentBD);

        // Frequent questions
        if (agent.getFrequentQuestions() == null) {
            agent.setFrequentQuestions(new ArrayList<>());
        }

        if (agentBD.getFrequentQuestions() != null) {
            for (FrequentQuestion frequentQuestionBD : agentBD.getFrequentQuestions()) {
                boolean found = false;

                for (FrequentQuestionBean frequentQuestionBean : agent.getFrequentQuestions()) {
                    if (frequentQuestionBean.getId() != null && frequentQuestionBD.getId().compareTo(frequentQuestionBean.getId()) == 0) {
                        found = true;
                        break;
                    }
                }

                if (!found) {
                    frequentQuestionRepository.delete(frequentQuestionBD);
                }
            }
        }

        boolean changeToPendingSynchronized = false;

        for (FrequentQuestionBean frequentQuestionBean : agent.getFrequentQuestions()) {
            FrequentQuestion frequentQuestionBD = null;
            boolean existInDB = false;

            if (frequentQuestionBean.getId() != null) {
                Optional<FrequentQuestion> frequentQuestionFound = frequentQuestionRepository.findById(frequentQuestionBean.getId());

                if (frequentQuestionFound.isPresent()) {
                    existInDB = true;
                    frequentQuestionBD = frequentQuestionFound.get();
                }
            }

            if (frequentQuestionBD == null) {
                frequentQuestionBD = new FrequentQuestion();
                frequentQuestionBD.setStatus(FrequentQuestionStatusEnum.PENDING_SYNCHRONIZED);
                frequentQuestionBD.setUuid(frequentQuestionBean.getUuid());

                changeToPendingSynchronized = true;
            }

            frequentQuestionBD.setAgent(agentBD);
            frequentQuestionBD.setDescription(frequentQuestionBean.getDescription());
            frequentQuestionBD.setFilename(frequentQuestionBean.getFilename());
            frequentQuestionBD.setStatus(FrequentQuestionStatusEnum.fromString(frequentQuestionBean.getStatus()));
            frequentQuestionBD.setUser(frequentQuestionBean.getUser());

            if (!existInDB) {
                LocalDateTime uploadDate = questionFileRepository.findUploadDateByUuid(frequentQuestionBean.getUuid());
                frequentQuestionBD.setUploadDate(uploadDate);
            }

            frequentQuestionRepository.save(frequentQuestionBD);

        }

        if (changeToPendingSynchronized) {
            agentBD.setStatus(AgentStatusEnum.PENDING_SYNCHRONIZED);
            agentBD = agentRepository.save(agentBD);
        }

        // Deployment channels
        if (agent.getDeploymentChannels() == null) {
            agent.setDeploymentChannels(new ArrayList<>());
        }

        if (agentBD.getDeploymentChannels() != null) {
            for (DeploymentChannel deploymentChannelBD : agentBD.getDeploymentChannels()) {
                boolean found = false;

                for (DeploymentChannelBean deploymentChannelBean : agent.getDeploymentChannels()) {
                    if (deploymentChannelBean.getId() != null && deploymentChannelBD.getId().compareTo(deploymentChannelBean.getId()) == 0) {
                        found = true;
                        break;
                    }
                }

                if (!found) {
                    deploymentChannelParamValueRepository.deleteAll(deploymentChannelBD.getParameters());
                    deploymentChannelRepository.delete(deploymentChannelBD);
                }
            }
        }

        for (DeploymentChannelBean deploymentChannelBean : agent.getDeploymentChannels()) {
            DeploymentChannel deploymentChannelBD = null;
            boolean existInBD = false;

            if (deploymentChannelBean.getId() != null) {
                Optional<DeploymentChannel> deploymentChannelFound = deploymentChannelRepository.findById(deploymentChannelBean.getId());

                if (deploymentChannelFound.isPresent()) {
                    deploymentChannelBD = deploymentChannelFound.get();
                    existInBD = true;
                }
            }

            if (deploymentChannelBD == null) {
                deploymentChannelBD = new DeploymentChannel();
            }

            deploymentChannelBD.setAgent(agentBD);
            deploymentChannelBD.setStatus(StatusEnum.fromString(deploymentChannelBean.getStatus()));

            if (deploymentChannelBean.getChannelId() != null) {
                Optional<Channel> channelFound = channelRepository.findById(deploymentChannelBean.getChannelId());

                if (channelFound.isPresent()) {
                    deploymentChannelBD.setChannel(channelFound.get());
                    if (!existInBD) {
                        deploymentChannelBD.setName(agentBD.getName() + "_" + channelFound.get().getName());
                    }
                }
            } else {
                deploymentChannelBD.setName(agentBD.getName() + "_" + UUID.randomUUID());
                deploymentChannelBD.setChannel(null);
            }
            deploymentChannelRepository.save(deploymentChannelBD);

            // Deployment Channel parameters

            if (deploymentChannelBean.getParameters() == null) {
                deploymentChannelBean.setParameters(new ArrayList<>());
            }

            if (deploymentChannelBD.getParameters() != null) {
                for (DeploymentChannelParamValue deploymentChannelParamValueBD : deploymentChannelBD.getParameters()) {
                    boolean found = false;

                    for (DeploymentChannelParamValueBean deploymentChannelParamValueBean : deploymentChannelBean.getParameters()) {
                        if (deploymentChannelParamValueBean.getId() != null &&
                                deploymentChannelParamValueBD.getId().compareTo(deploymentChannelParamValueBean.getId()) == 0) {
                            found = true;
                            break;
                        }
                    }

                    if (!found) {
                        deploymentChannelParamValueRepository.delete(deploymentChannelParamValueBD);
                    }
                }
            }

            for (DeploymentChannelParamValueBean deploymentChannelParamValueBean : deploymentChannelBean.getParameters()) {
                DeploymentChannelParamValue deploymentChannelParamValue = null;

                if (deploymentChannelParamValueBean.getId() != null) {
                    Optional<DeploymentChannelParamValue> deploymentChannelParamValueFound = deploymentChannelParamValueRepository
                            .findById(deploymentChannelParamValueBean.getId());

                    if (deploymentChannelParamValueFound.isPresent()) {
                        deploymentChannelParamValue = deploymentChannelParamValueFound.get();
                    }
                }

                if (deploymentChannelParamValue == null) {
                    deploymentChannelParamValue = new DeploymentChannelParamValue();
                }

                deploymentChannelParamValue.setDeploymentChannel(deploymentChannelBD);
                deploymentChannelParamValue.setValue(deploymentChannelParamValueBean.getValue());

                if (deploymentChannelParamValueBean.getChannelParamName() != null) {
                    Optional<ChannelParam> channelParamFound = channelParamRepository.findByNameAndChannelId(
                            deploymentChannelParamValueBean.getChannelParamName(), deploymentChannelBean.getChannelId());

                    if (channelParamFound.isPresent()) {
                        deploymentChannelParamValue.setParameter(channelParamFound.get());
                    }
                } else {
                    deploymentChannelParamValue.setParameter(null);
                }
                deploymentChannelParamValueRepository.save(deploymentChannelParamValue);
            }

        }

    }

    public AgentBean getAgent(Long id) {

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");

        AgentBean bean = null;

        Optional<Agent> agentFound = agentRepository.findById(id);

        if (agentFound.isPresent()) {
            Agent agent = agentFound.get();

            bean = new AgentBean();

            bean.setId(agent.getId());
            bean.setName(agent.getName());
            bean.setDescription(agent.getDescription());
            bean.setVersion(agent.getVersion());
            bean.setLanguage(agent.getLanguage().getName());
            bean.setTimezone(agent.getTimezone());
            bean.setType(agent.getType().getName());
            bean.setStatus(agent.getStatus().getName());
            bean.setAvatar(agent.getAvatar());
            bean.setPeriod(agent.getPeriod());

            if (agent.getCountry() != null) {
                bean.setCountryName(agent.getCountry().getName());
                bean.setCountryId(agent.getCountry().getId());
            }

            // Deployment channel

            bean.setFrequentQuestions(new ArrayList<>());

            for (FrequentQuestion frequentQuestion : agent.getFrequentQuestions()) {
                FrequentQuestionBean frequentQuestionBean = new FrequentQuestionBean();
                frequentQuestionBean.setId(frequentQuestion.getId());
                frequentQuestionBean.setUuid(frequentQuestion.getUuid());
                frequentQuestionBean.setDescription(frequentQuestion.getDescription());
                frequentQuestionBean.setUser(frequentQuestion.getUser());
                frequentQuestionBean.setFilename(frequentQuestion.getFilename());
                frequentQuestionBean.setStatus(frequentQuestion.getStatus().getName());

                if(frequentQuestion.getUploadDate() != null) {
                    frequentQuestionBean.setUploadDate(frequentQuestion.getUploadDate().format(formatter));
                }

                bean.getFrequentQuestions().add(frequentQuestionBean);
            }

            // Deployment channel
            bean.setDeploymentChannels(new ArrayList<>());

            for (DeploymentChannel deploymentChannel : agent.getDeploymentChannels()) {
                DeploymentChannelBean deploymentChannelBean = new DeploymentChannelBean();
                deploymentChannelBean.setId(deploymentChannel.getId());
                deploymentChannelBean.setName(deploymentChannel.getName());
                deploymentChannelBean.setStatus(deploymentChannel.getStatus().getName());
                deploymentChannelBean.setParameters(new ArrayList<>());

                if (deploymentChannel.getChannel() != null) {
                    deploymentChannelBean.setChannelId(deploymentChannel.getChannel().getId());
                    deploymentChannelBean.setChannelName(deploymentChannel.getChannel().getName());
                }

                //Parameters
                List<DeploymentChannelParamValue> list = deploymentChannelParamValueRepository.getParameter(deploymentChannel.getId());
                for (DeploymentChannelParamValue parameter : list) {
                    DeploymentChannelParamValueBean parameterBean = new DeploymentChannelParamValueBean();
                    parameterBean.setId(parameter.getId());
                    parameterBean.setValue(parameter.getValue());

                    if (parameter.getParameter() != null) {
                        parameterBean.setChannelParamName(parameter.getParameter().getName());
                    }

                    deploymentChannelBean.getParameters().add(parameterBean);
                }

                bean.getDeploymentChannels().add(deploymentChannelBean);
            }
        }

        return bean;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public boolean delete(Long id) {
        boolean isValid = true;

        if (id != null) {
            Optional<Agent> agentFound = agentRepository.findById(id);

            if (agentFound.isPresent()) {
                Agent agent = agentFound.get();
                agent.setStatus(AgentStatusEnum.DELETED);
                scheduleService.cancelTaskDelete(id);
                agentRepository.save(agent);
            }
        }
        else{
            isValid = false;
        }

        return isValid;
    }
    
    public List<CountryBean> getCountries() {
        List<CountryBean> countriesBean = new ArrayList<>();
        List<Country> countries = countryRepository.findAllOrder();

        for (Country country : countries) {
            countriesBean.add(CountryBean.clone(country));
        }

        return countriesBean;
    }

    public List<ChannelBean> getChannels() {
        List<ChannelBean> channelsBean = new ArrayList<>();
        List<Channel> channels = (List<Channel>) channelRepository.findAll();

        for (Channel channel : channels) {
            List<ChannelParam> channelParamList = channel.getParameters();
            for (ChannelParam channelParam : channelParamList) {
                channelParam.setTraductions(channelParam.getTraductions().replace("\\", ""));
            }
            channelsBean.add(ChannelBean.clone(channel));
        }

        return channelsBean;
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void synchronizeFiles(Long id, String user) {
        if (AppContextManager.getAppContext() == null)  {
            return;
        }
        FileConverter fileConverter = (FileConverter) AppContextManager.getAppContext().getBean("xlsToCsvFileConverter");

        List<FrequentQuestion> frequentQuestions = frequentQuestionRepository.findAllByAgentIdAndStatus(id, FrequentQuestionStatusEnum.PENDING_SYNCHRONIZED);

        Map<String, Object> headers = new HashMap<>();

        for (FrequentQuestion frequentQuestion : frequentQuestions) {

            Optional<QuestionFile> questionFileFound = questionFileRepository.findByUuid(frequentQuestion.getUuid());

            if (questionFileFound.isPresent()) {
                headers.put("CamelFileName", fileConverter.getFileName(questionFileFound.get().getName()));
//                try {
//                    FileUtils.writeByteArrayToFile(new File("/home/schicoma/Escritorio/" + questionFileFound.get().getName()), questionFileFound.get().getData());
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
                producerTemplate.sendBodyAndHeaders("direct:transferFile", fileConverter.convert(questionFileFound.get().getData(), questionFileFound.get().getName()), headers);
            }

            frequentQuestion.setStatus(FrequentQuestionStatusEnum.UPLOADED);
            //frequentQuestion.setUser(user);
            //frequentQuestion.setUploadDate(LocalDateTime.now());
            frequentQuestionRepository.save(frequentQuestion);
        }

        Optional<Agent> agentFound = agentRepository.findById(id);

        if (agentFound.isPresent()) {
            Agent agent = agentFound.get();
            agent.setStatus(AgentStatusEnum.DEPLOYED);
            agentRepository.save(agent);
        }

    }

    public List<Map> getAgentAll() {
        List<Agent> lista = (List<Agent>) agentRepository.findAll();
        if (lista.size() <= 0) {
            throw new NotFoundException("Error al listar agentes.");
        }
        List<Map> result = new ArrayList<>();
        for(Agent agent: lista) {
            Map<String, Object> map = new HashMap<>();
            map.put("id", agent.getId());
            map.put("name", agent.getName());
            map.put("description", agent.getDescription());
            map.put("version", agent.getVersion());
            map.put("status", agent.getStatus().getName());
            map.put("period", agent.getPeriod());
            map.put("timezone", agent.getTimezone());
            result.add(map);
        }
        return result;
    }

}
