package com.publiccms.views.directive.cms;

// Generated 2015-5-10 17:54:56 by com.publiccms.common.generator.SourceGenerator

import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

import com.publiccms.common.base.AbstractTemplateDirective;
import com.publiccms.common.base.HighLighterQuery;
import com.publiccms.common.handler.PageHandler;
import com.publiccms.common.handler.RenderHandler;
import com.publiccms.common.tools.CmsLangUtils;
import com.publiccms.common.tools.CmsUrlUtils;
import com.publiccms.common.tools.CommonUtils;
import com.publiccms.common.tools.ExtendUtils;
import com.publiccms.common.tools.RequestUtils;
import com.publiccms.entities.cms.CmsContent;
import com.publiccms.entities.cms.CmsContentAttribute;
import com.publiccms.entities.cms.CmsContentLang;
import com.publiccms.entities.cms.CmsContentLangId;
import com.publiccms.entities.sys.SysSite;
import com.publiccms.logic.component.config.ContentConfigComponent;
import com.publiccms.logic.component.config.ContentConfigComponent.KeywordsConfig;
import com.publiccms.logic.component.config.SiteAttributeComponent;
import com.publiccms.logic.component.site.FileUploadComponent;
import com.publiccms.logic.component.site.StatisticsComponent;
import com.publiccms.logic.service.cms.CmsContentAttributeService;
import com.publiccms.logic.service.cms.CmsContentLangService;
import com.publiccms.logic.service.cms.CmsContentService;
import com.publiccms.views.pojo.entities.ClickStatistics;
import com.publiccms.views.pojo.query.CmsContentSearchQuery;

import freemarker.template.TemplateException;
import freemarker.template.TemplateModelException;

/**
 *
 * search 内容列表查询指令
 * <p>
 * 上下文变量
 * <ul>
 * <li><code>lang</code>:语言
 * </ul>
 * <p>
 * 参数列表
 * <ul>
 * <li><code>word</code>:搜索词,多个搜索词时取并集结果
 * <li><code>exclude</code>:排除词汇
 * <li><code>tagIds</code>:多个标签id,多个标签时取并集结果
 * <li><code>userId</code>:用户id
 * <li><code>parentId</code>:父内容id
 * <li><code>categoryId</code>:分类id
 * <li><code>containChild</code>:包含子分类,当categoryId不为空时有效
 * <li><code>categoryIds</code>:多个分类id,当categoryId为空时有效
 * <li><code>modelIds</code>:多个模型id
 * <li><code>extendsValues</code>
 * 多个全文搜索字段値,格式：[字段编码]:字段値],例如:extendsValues='isbn:value1,unicode:value2'
 * <li><code>dictionaryValues</code>
 * 多个字典搜索字段値,只有数据字典父级値時包含所有子级结果,格式：[字段编码]_[字段値],例如:dictionaryValues='extend1_value1,extend1_value2'
 * <li><code>dictionaryUnion</code>
 * 取数据字典并集结果,dictionaryUnion不为空时有效,【true,false】,默认为交集结果
 * <li><code>highlight</code>:高亮关键词,【true,false】,默认为false,启用高亮后,
 * 标题、作者、编辑、描述字段应该加?no_esc以使高亮html生效,cms会自动对原值有进行html安全转义
 * <li><code>preTag</code>:高亮前缀,启用高亮时有效,默认为"&lt;B&gt;"
 * <li><code>postTag</code>:高亮后缀,启用高亮时有效,默认为"&lt;/B&gt;"
 * <li><code>projection</code>:投影结果,【true,false】,默认为false
 * <li><code>phrase</code>:精确搜索,【true,false】,默认为false
 * <li><code>fields</code>:搜索字段,【title:标题, author:作者, editor:编辑, description:描述,
 * text:正文,files:附件】
 * <li><code>containsAttribute</code>默认为<code>false</code>,http请求时为高级选项,为true时<code>content.attribute</code>为内容扩展数据<code>map</code>(字段编码,<code>value</code>)
 * <li><code>startPublishDate</code>:起始发布日期,【2000-01-01 23:59:59】,【2000-01-01】
 * <li><code>endPublishDate</code>:终止发布日期,【2000-01-01 23:59:59】,【2000-01-01】
 * <li><code>orderField</code>
 * 排序字段,【clicks:点击数倒序,score:分数倒序,publishDate:发布日期倒序,collections:收藏数倒叙,minPrice:最低价格,maxPrice:最高价格,extend.sort1-extend.sort10]:扩展字段排序标志】,默认相关度倒序
 * <li><code>pageIndex</code>:页码
 * <li><code>pageSize</code>:每页条数
 * <li><code>maxResults</code>:最大结果数
 * </ul>
 * <p>
 * 返回结果
 * <ul>
 * <li><code>page</code>:{@link com.publiccms.common.handler.PageHandler}
 * <li><code>page.list</code>:List类型 查询结果实体列表
 * {@link com.publiccms.entities.cms.CmsContent}
 * </ul>
 * 使用示例
 * <p>
 * &lt;#assign lang="cn"/&gt; &lt;@cms.search word='cms'
 * pageSize=10&gt;&lt;#list page.list as
 * a&gt;${a.title}&lt;#sep&gt;,&lt;/#list&gt;&lt;/@cms.search&gt;
 *
 * <pre>
*  &lt;script&gt;
  fetch('${site.dynamicPath}api/directive/cms/search?word=cms&amp;pageSize=10',{"headers":{"lang":"cn"}}).then(res => res.json()).then(data=>{
    console.log(data.page.totalCount);
  });
  &lt;/script&gt;
 * </pre>
 *
 */
@Component
public class CmsSearchDirective extends AbstractTemplateDirective {
    @Resource
    private CmsContentAttributeService attributeService;
    @Resource
    private StatisticsComponent statisticsComponent;
    @Resource
    protected ContentConfigComponent contentConfigComponent;
    @Resource
    protected SiteAttributeComponent siteAttributeComponent;
    @Resource
    protected FileUploadComponent fileUploadComponent;

    @Override
    public void execute(RenderHandler handler) throws IOException, TemplateException {
        handler.put("page", query(handler, handler.getBoolean("factSearch", false))).render();
    }

    public PageHandler query(RenderHandler handler, boolean factSearch) throws TemplateModelException {
        String word = handler.getString("word");
        Long[] tagIds = handler.getLongArray("tagIds");
        if (null == tagIds) {
            tagIds = handler.getLongArray("tagId");
        }
        SysSite site = getSite(handler);
        String lang = handler.getStringAttribute("lang");
        if (CommonUtils.notEmpty(word)) {
            word = CommonUtils.keep(word, 100, null);
            String ip = RequestUtils.getIpAddress(handler.getRequest());
            statisticsComponent.search(site.getId(), word, lang, ip);
        }
        if (CommonUtils.notEmpty(tagIds)) {
            for (Long tagId : tagIds) {
                statisticsComponent.searchTag(tagId);
            }
        }
        Integer pageIndex = handler.getInteger("pageIndex", 1);
        Integer pageSize = handler.getInteger("pageSize", handler.getInteger("count", 30));
        Date currentDate = CommonUtils.getMinuteDate();
        HighLighterQuery highLighterQuery = null;
        if (handler.getBoolean("highlight", false)) {
            highLighterQuery = new HighLighterQuery();
            highLighterQuery.setPreTag(handler.getString("preTag"));
            highLighterQuery.setPostTag(handler.getString("postTag"));
        }
        boolean containsAttribute = handler.getBoolean("containsAttribute", false);

        CmsContentSearchQuery query = new CmsContentSearchQuery(site.getId(), handler.getBoolean("projection", false),
                handler.getBoolean("phrase", false), word, handler.getString("exclude"), handler.getStringArray("fields"), tagIds,
                handler.getLong("userId"), handler.getLong("parentId"), handler.getInteger("categoryId"),
                handler.getIntegerArray("categoryIds"), handler.getStringArray("modelIds"),
                handler.getStringArray("extendsValues"), handler.getStringArray("dictionaryValues"),
                handler.getBoolean("dictionaryUnion"), handler.getDate("startPublishDate"),
                handler.getDate("endPublishDate", currentDate), currentDate);

        String defaultLang = siteAttributeComponent.getDefaultLanguage(site.getId());
        if (siteAttributeComponent.enableMultilingual(site.getId())) {
            if (null != lang && !lang.equalsIgnoreCase(defaultLang)) {
                query.setLang(lang);
            }
        }
        containsAttribute = handler.inHttp() ? getAdvanced(handler) && containsAttribute : containsAttribute;
        try {

            PageHandler page = null;
            if (factSearch) {
                page = service.facetQuery(query, handler.getBoolean("containChild"), handler.getString("orderField"),
                        handler.getString("orderType"), pageIndex, pageSize, handler.getInteger("maxResults"));
            } else {
                page = service.query(query, handler.getBoolean("containChild"), handler.getString("orderField"),
                        handler.getString("orderType"), pageIndex, pageSize, handler.getInteger("maxResults"));
            }

            @SuppressWarnings("unchecked")
            List<CmsContent> list = (List<CmsContent>) page.getList();
            if (null != list) {
                CmsContentLangId[] langIds = null;
                if (CommonUtils.notEmpty(lang)) {
                    langIds = list.stream().map(e -> new CmsContentLangId(e.getId(), lang)).toArray(CmsContentLangId[]::new);
                }
                Map<Long, CmsContentLang> langMap = CommonUtils.listToMap(langService.getEntitys(langIds),
                        k -> k.getId().getContentId());

                Consumer<CmsContent> consumer = null;
                if (containsAttribute) {
                    Long[] ids = list.stream().map(CmsContent::getId).toArray(Long[]::new);
                    List<CmsContentAttribute> attributeList = attributeService.getEntitys(ids);
                    KeywordsConfig config = contentConfigComponent.getKeywordsConfig(site.getId());
                    Map<Object, CmsContentAttribute> attributeMap = CommonUtils.listToMap(attributeList, k -> k.getContentId());
                    consumer = e -> {
                        ClickStatistics statistics = statisticsComponent.getContentStatistics(e.getId());
                        if (null != statistics) {
                            e.setClicks(e.getClicks() + statistics.getClicks());
                        }
                        CmsContentLang langEntity = null;
                        CmsContentAttribute attribute = attributeMap.get(e.getId());
                        if (CommonUtils.notEmpty(lang) && !lang.equalsIgnoreCase(e.getLang())) {
                            langEntity = langMap.get(e.getId());
                            CmsLangUtils.initLang(e, defaultLang, langEntity);
                            CmsLangUtils.initLang(attribute, langEntity);
                        }
                        CmsUrlUtils.initContentUrl(site, e);
                        fileUploadComponent.initContentCover(site, e);
                        e.setAttribute(ExtendUtils.getAttributeMap(attribute, config));
                    };
                } else {
                    consumer = e -> {
                        ClickStatistics statistics = statisticsComponent.getContentStatistics(e.getId());
                        if (null != statistics) {
                            e.setClicks(e.getClicks() + statistics.getClicks());
                        }
                        CmsContentLang langEntity = null;
                        if (CommonUtils.notEmpty(lang) && !lang.equalsIgnoreCase(e.getLang())) {
                            langEntity = langMap.get(e.getId());
                            CmsLangUtils.initLang(e, defaultLang, langEntity);
                        }
                        CmsUrlUtils.initContentUrl(site, e);
                        fileUploadComponent.initContentCover(site, e);
                    };
                }
                list.forEach(consumer);
                if (null != highLighterQuery) {
                    service.higtLighter(list, word, highLighterQuery);
                }
            }
            return page;
        } catch (Exception e) {
            log.error(e.getMessage());
            PageHandler page = new PageHandler(pageIndex, pageSize);
            page.setList(Collections.emptyList());
            return page;
        }
    }

    @Override
    public boolean supportAdvanced() {
        return true;
    }

    @Resource
    private CmsContentService service;
    @Resource
    private CmsContentLangService langService;
}