package com.uas.search.controller;

import com.uas.search.util.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.uas.search.constant.model.CollectField;
import com.uas.search.constant.model.PageParams;
import com.uas.search.constant.model.PageParams.FilterField;
import com.uas.search.constant.model.SPage;
import com.uas.search.dao.ComponentDao;
import com.uas.search.exception.SearchException;
import com.uas.search.model.Brand;
import com.uas.search.model.Component;
import com.uas.search.model.Goods;
import com.uas.search.model.Kind;
import com.uas.search.service.SearchService;
import com.uas.search.service.impl.IndexServiceImpl;
import com.uas.search.util.FileUtils;
import com.uas.search.util.SearchUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import com.uas.search.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.*;
import java.util.Map.Entry;

/**
 * 搜索请求
 *
 * @author suntg
 * @since 2016年8月1日上午9:18:05
 */
@Controller
@RequestMapping("/search")
public class SearchController {

	@Autowired
	private SearchService searchService;

	private Logger logger = LoggerFactory.getLogger(getClass());

	@RequestMapping("/kindIds")
	@ResponseBody
	public SPage<Long> seachKindIds(@RequestParam String keyword, Integer page, Integer size,
									HttpServletRequest request) {
		return searchService.getKindIds(keyword, page, size);
	}

	@RequestMapping("/kinds")
	@ResponseBody
	public SPage<Map<String, Object>> seachKinds(@RequestParam String keyword, Integer page, Integer size,
												 HttpServletRequest request) {
		return searchService.getKinds(keyword, page, size);
	}

	@RequestMapping("/brandIds")
	@ResponseBody
	public SPage<Long> searchBrandIds(@RequestParam String keyword, Integer page, Integer size,
									  HttpServletRequest request) {
		return searchService.getBrandIds(keyword, page, size);
	}

	@RequestMapping("/brands")
	@ResponseBody
	public SPage<Map<String, Object>> searchBrand(@RequestParam String keyword, Integer page, Integer size,
												  HttpServletRequest request) {
		return searchService.getBrands(keyword, page, size);
	}

    @RequestMapping("/componentIds")
    @ResponseBody
    public Map<String, Object> searchComponentIds(@RequestParam String keyword, String params, HttpServletRequest request) {
        PageParams pageParams = params==null?null:JSONObject.parseObject(params, PageParams.class);
        return searchService.getComponentIds(keyword, pageParams);
    }

	@Autowired
	private ComponentDao componentDao;

    @RequestMapping("/components")
    @ResponseBody
    public List<Component> searchComponents(String keyword, String params, HttpServletRequest request) {
        @SuppressWarnings("unchecked")
        List<Long> ids = (List<Long>) searchComponentIds(keyword, params, request).get("componentIds");
        return findComponents(ids);
    }

    private List<Component> findComponents(List<Long> ids){
        if (CollectionUtils.isEmpty(ids)) {
            return new ArrayList<>();
        }

        Long[] idsLong = new Long[ids.size()];
        int i = 0;
        for (Long id : ids) {
            idsLong[i++] = id;
        }
        return componentDao.findAll(ids);
    }

	@RequestMapping("/kindIdsByComponent")
	@ResponseBody
	public Set<Long> searchKindIdsBySearchComponent(String keyword, String brandId, HttpServletRequest request) {
		return searchService.getKindIdsBySearchComponent(keyword, brandId);
	}

	@RequestMapping("/kindsByComponent")
	@ResponseBody
	public Set<Map<String, Object>> searchKindsBySearchComponent(String keyword, String brandId,
																  HttpServletRequest request) {
		return searchService.getKindsBySearchComponent(keyword, brandId);
	}

	@RequestMapping("/brandIdsByComponent")
	@ResponseBody
	public Set<Long> searchBrandIdsBySearchComponent(String keyword, String kindId, HttpServletRequest request) {
		return searchService.getBrandIdsBySearchComponent(keyword, kindId);
	}

	@RequestMapping("/brandsByComponent")
	@ResponseBody
	public Set<Map<String, Object>> searchBrandsBySearchComponent(String keyword, String kindId,
																   HttpServletRequest request) {
		return searchService.getBrandsBySearchComponent(keyword, kindId);
	}

	@RequestMapping("/similarKeywords")
	@ResponseBody
	public List<String> getSimilarKeywords(@RequestParam String keyword, Integer size, HttpServletRequest request) {
		return searchService.getSimilarKeywords(keyword, size);
	}

	@RequestMapping("/similarComponents")
	@ResponseBody
	public List<Map<String, Object>> getSimilarComponents(@RequestParam String keyword, Integer size,
														  HttpServletRequest request) {
		return searchService.getSimilarComponents(keyword, size);
	}

	@RequestMapping("/similarBrands")
	@ResponseBody
	public List<Map<String, Object>> getSimilarBrands(@RequestParam String keyword, Integer size, HttpServletRequest request) {
		return searchService.getSimilarBrands(keyword, size);
	}

	@RequestMapping("/similarKinds")
	@ResponseBody
	public List<Map<String, Object>> getSimilarKinds(@RequestParam String keyword, Integer size, HttpServletRequest request) {
		return searchService.getSimilarKinds(keyword, size);
	}

	@RequestMapping("/similarLeafKinds")
	@ResponseBody
	public List<Map<String, Object>> getSimilarLeafKinds(@RequestParam String keyword, Integer size, HttpServletRequest request) {
		return searchService.getSimilarLeafKinds(keyword, size);
	}

	@RequestMapping("/similarKindsByLevel")
	@ResponseBody
	public List<Map<String, Object>> getSimilarKindsByLevel(@RequestParam String keyword, Short level, Integer size, HttpServletRequest request) {
		return searchService.getSimilarKindsByLevel(keyword, level, size);
	}

	@RequestMapping("/similarPropertyValues")
	@ResponseBody
	public List<Map<String, String>> getSimilarPropertyValues(Long kindId, Long propertyId, String keyword, Long topNum,
															  HttpServletRequest request) {
		return searchService.getSimilarPropertyValues(kindId, propertyId, keyword, topNum);
	}

    public Map<FilterField, Object> parseFilters(JSONObject json){
        Map<FilterField, Object> filters = new HashMap<>();
        if(json!=null){
            Set<Entry<String, Object>> entrySet = json.entrySet();
            for (Entry<String, Object> entry : entrySet) {
                FilterField field = FilterField.valueOf(entry.getKey());
                String value = entry.getValue().toString();
                switch (field) {
                    case GOODS_KINDID:
                    case GOODS_BRANDID:
                    case GOODS_STORE_TYPE:
                    case GOODS_CRNAME:
                        String[] strs = value.split(",");
                        List<Object> values = new ArrayList<>();
                        for (String str : strs) {
                            values.add(str);
                        }
                        filters.put(field, values);
                        break;
                    default:
                        filters.put(field, value);
                }
            }
        }
        return filters;
    }

	@RequestMapping("/goodsIds")
	@ResponseBody
	public Map<String, Object> getGoodsIds(String keyword, String params, HttpServletRequest request) {
		PageParams pageParams = params==null?null:JSONObject.parseObject(params, PageParams.class);
		return searchService.getGoodsIds(keyword, pageParams);
	}

	@RequestMapping("/collectBySearchGoods")
	@ResponseBody
	public List<Map<String, Object>> collectBySearchGoods(String keyword, @RequestParam String collectedField, String filters, HttpServletRequest request) {
        Map<FilterField, Object> filtersMap = new HashMap<>();
        if(!StringUtils.isEmpty(filters)){
            JSONObject json = JSONObject.parseObject(filters);
            Set<Entry<String, Object>> entrySet = json.entrySet();
            for (Entry<String, Object> entry : entrySet) {
                filtersMap.put(FilterField.valueOf(entry.getKey().toUpperCase()), entry.getValue());
            }
        }
		return searchService.collectBySearchGoods(keyword, CollectField.valueOf(collectedField.toUpperCase()),
                filtersMap);
	}

	@RequestMapping("/kind/{id}")
	@ResponseBody
	public Kind getKind(@PathVariable Long id, HttpServletRequest request) {
		return searchService.getKind(id);
	}

	@RequestMapping("/brand/{id}")
	@ResponseBody
	public Brand getBrand(@PathVariable Long id, HttpServletRequest request) {
		return searchService.getBrand(id);
	}

	@RequestMapping("/component/{id}")
	@ResponseBody
	public Component getComponent(@PathVariable Long id, HttpServletRequest request) {
		return searchService.getComponent(id);
	}

	@RequestMapping("/goods/{id}")
	@ResponseBody
	public Goods getGoods(@PathVariable String id, HttpServletRequest request) {
		return searchService.getGoods(id);
	}

	@RequestMapping("/objects")
	@ResponseBody
	public SPage<Object> getObjects(@RequestParam String tableName, String keyword, String field,
									Boolean tokenized, Integer page, Integer size, HttpServletRequest request) {
		return searchService.getObjects(tableName.toLowerCase(), keyword, field, tokenized, page == null ? 0 : page,
				size == null ? 0 : size);
	}

	@RequestMapping("/allObjectsToFiles")
	@ResponseBody
	public String writeAllObjectsToFiles(@RequestParam String tableName, HttpServletRequest request) {
		int page = 1;
		int size = 1000;
		// 不能边更新索引边分页获取索引中的数据，因为索引更新后，分页顺序可能也会变化，
		// 所以要先把数据保存到本地，等待全部获取之后重建索引
		Long startTime = new Date().getTime();
		// 先删除旧的文件
		FileUtils.deleteSubFiles(new File(SearchUtils.getDataPath(tableName)));

		SPage<Object> sPage = searchService.getObjects(tableName.toLowerCase(), null, null, null, page, size);
		// 索引中数据的总数目
		long totalElements = sPage.getTotalElement();
		logger.info("发现数据：" + totalElements + "条");
		int fileIndex = 1;
		PrintWriter printWriter = null;
		int count = 0;
		try {
			File file = new File(SearchUtils.getDataPath(tableName));
			if (!file.exists()) {
				file.mkdirs();
			}
			printWriter = new PrintWriter(SearchUtils.getDataPath(tableName) + "/" + fileIndex + ".txt");
			while (true) {
				// 一个文件存放100000条数据，一旦超过，写入新的文件
				if (count > IndexServiceImpl.SINGLE_FILE_MAX_SIZE) {
					count = 1;
					printWriter.flush();
					printWriter.close();
					fileIndex++;
					printWriter = new PrintWriter(SearchUtils.getDataPath(tableName) + "/" + fileIndex + ".txt");
				}
				List<Object> content = sPage.getContent();
				for (Object element : content) {
					printWriter.println(JSONObject.toJSONString(element));
					count++;
				}
				logger.info(String.format(tableName + "...................%.2f%%",
						((page - 1) * size + content.size()) * 100.0 / totalElements));

				if (++page > sPage.getTotalPage()) {
					break;
				}
				sPage = searchService.getObjects(tableName.toLowerCase(), null, null, null, page, size);
			}
			printWriter.flush();
			printWriter.close();
		} catch (FileNotFoundException | SecurityException | IllegalArgumentException e) {
			throw new SearchException(e).setDetailedMessage(e);
		}
		Long endTime = new Date().getTime();
		String message = String.format("写入数据%s条，耗时%.2fs\n ", totalElements, (endTime - startTime) / 1000.0);
		logger.info(message);
		return message;
	}
}
