
package org.bisen.blog.service;

import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.apache.commons.dbcp.BasicDataSource;
import org.bisen.blog.model.Blog;
import org.bisen.blog.model.BlogUser;
import org.bisen.blog.model.ElasticBlog;
import org.bisen.blog.repository.elasticsearch.ElasticBlogRepository;
import org.bisen.blog.repository.springdatajpa.BlogRepository;
import org.bisen.blog.repository.springdatajpa.UserRepository;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.base.Strings;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.highlight.HighlightBuilder;
import org.jooq.DSLContext;
import org.jooq.impl.DSL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.DefaultEntityMapper;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.EntityMapper;
import org.springframework.data.elasticsearch.core.FacetedPage;
import org.springframework.data.elasticsearch.core.FacetedPageImpl;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.query.MoreLikeThisQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 *
 * @author asikprad
 */
@Service("blogService")
public class BlogService {

    @Autowired
    @Qualifier("blogRepository")
    private BlogRepository blogRepository;

    @Autowired
    @Qualifier("userRepository")
    private UserRepository userRepository;

    @Autowired(required = false)
    @Qualifier("elasticBlogRepository")
    private ElasticBlogRepository elasticBlogRepository;

    @Autowired
    DSLContext create;
    @Autowired
    @Qualifier("dataSource")
    private BasicDataSource dataSource;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Transactional(readOnly = true)
    public Blog find(int blogId) throws DataAccessException {
        return blogRepository.findOne(blogId);
    }

    @Transactional
    private Blog saveToDatabase(Blog blog) {
        return blogRepository.save(blog);
    }

    @Transactional
    private ElasticBlog saveToElastic(Blog blog) {
        return elasticBlogRepository.save(new ElasticBlog.Builder(blog).build());
    }

    public ElasticBlog save(Blog blog) throws DataAccessException {
        Blog databaseBlog = saveToDatabase(blog);
        return saveToElastic(databaseBlog);
    }

    @Transactional
    public BlogUser saveUser(BlogUser user) throws DataAccessException {
        return userRepository.save(user);
    }

    @Transactional(readOnly = true)
    public BlogUser findUser(String email) throws DataAccessException {
        String sql = create.select().from(DSL.tableByName("user")).where(DSL.fieldByName("email").eq(email)).getSQL();
        JdbcTemplate template = new JdbcTemplate(dataSource);
        BlogUser user = template.query(sql, new Object[]{email}, (ResultSet rs) -> rs.next() ? new BlogUser(rs.getString("email"), rs.getString("full_name")) : null);
        return user;
    }

    @Transactional(readOnly = true)
    public Page<ElasticBlog> findAll(Pageable pageable) throws DataAccessException {
        return elasticBlogRepository.findAll(pageable);
    }

    @Transactional(readOnly = true)
    public Page<ElasticBlog> moreLikeThis(String id) {
        MoreLikeThisQuery query = new MoreLikeThisQuery();
        query.setId(id);
        query.addFields("strippedContent");
        query.setMinDocFreq(1);
        return elasticsearchTemplate.moreLikeThis(query, ElasticBlog.class);
        
    }
    @Transactional(readOnly = true)
    public Page<ElasticBlog> search(String query, Pageable pageable) throws DataAccessException {

        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.queryString(query))
                .withPageable(pageable)
                .withHighlightFields(new HighlightBuilder.Field("strippedContent"), new HighlightBuilder.Field("title"))
                .build();
        Page<ElasticBlog> pageResult = elasticsearchTemplate.queryForPage(searchQuery, ElasticBlog.class, new SearchResultMapper() {

            @Override
            public <T> FacetedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                List<ElasticBlog> blogs = new ArrayList<>();
                for (SearchHit hit : response.getHits()) {
                    if (response.getHits().getHits().length <= 0) {
                        return null;
                    }
                    ElasticBlog blog = new ElasticBlog();
                    blog.setId(Integer.parseInt(hit.getId()));
                    blog.setContent((String) hit.getSource().get("content"));
                    blog.setCreatedByEmail((String) hit.getSource().get("createdByEmail"));
                    blog.setCreatedDate(hit.getSource().get("createdDate") != null
                            ? new Date((long) hit.getSource().get("createdDate")) : null);
                    blog.setLastModifiedByEmail((String) hit.getSource().get("createdModifiedEmail"));
                    blog.setLastModifiedDate(hit.getSource().get("lastModifiedDate") != null
                            ? new Date((long) hit.getSource().get("lastModifiedDate")) : null);
                    blog.setTags((ArrayList<String>) hit.getSource().get("tags"));
                    blog.setTitle((String) hit.getSource().get("title"));
                    if (hit.getHighlightFields() != null) {
                        if (hit.getHighlightFields().get("strippedContent") != null) {
                            blog.setHighlightedString(hit.getHighlightFields().get("strippedContent").fragments()[0].toString());
                        } else {
                            blog.setHighlightedString(hit.getHighlightFields().get("title").fragments()[0].toString());
                        }

                    }

                    blogs.add(blog);
                }
                if (blogs.size() > 0) {
                    FacetedPage<T> page = new FacetedPageImpl<>((List<T>) blogs);
                    return page;
                }
                return null;
            }
        });
        return pageResult;
    }

}
