package com.huigou.topsun.product.application.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.huigou.topsun.product.application.ProductApplication;
import com.huigou.topsun.product.domain.*;
import com.huigou.topsun.product.repository.*;
import com.huigou.topsun.util.Snowflake;
import lombok.RequiredArgsConstructor;
import org.apache.commons.beanutils.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

// @RequiredArgsConstructor 通过构造方法注入 ProductApplication(Class a) { this.a = a; }
/**
 * @author 16508
 * @description 针对表【product(产品)】的数据库操作Service实现
 * @createDate 2023-11-22 10:24:31
 */
@Service
@RequiredArgsConstructor
public class ProductApplicationImpl implements ProductApplication {

    @PersistenceContext(unitName = "system")
    private EntityManager entityManager;

//    private final EntityManagerFactory entityManagerFactory;
    private final ObjectMapper objectMapper;
    private final  ProductRepository productRepository;
    private final ProductDetailRepository detailRepository;

    // 类型

    private final BrandRepository brandRepository;
    private final FactoryRepository factoryRepository;
    private final ColorRepository colorRepository;
    private final ProductCategoryRepository categoryRepository;

    // 详细信息

    private final ProductTypesetConfRepository typesetConfRepository;
    private final ProductLookedRepository lookedRepository;
    private final ProductPublishedConfRepository publishedConfRepository;
    private final ProductLossRepository lossRepository;
    private final ProductMaterialRepository materialRepository;

    private final ProductFaceRepository faceRepository;
    private final ProductFaceColorRepository faceColorRepository;
    private final ProductTechnologyRepository technologyRepository;
    private final ProductProcessRepository processRepository;

    @Override
    public ArrayList<Map<String, Object>> findProductPage(int page, int size) {
//        PageRequest pageRequest = new PageRequest(page, size);
//        Page<Product> productPage = productRepository.findAll(pageRequest);
//        mapList.getContent().stream().map(Product::toString).forEach(System.out::println);
        ArrayList<Product> productPage = (ArrayList<Product>) productRepository.findAll();

//        convert = mapList.stream().collect(Collectors.toMap(index -> index, Function.identity(),
//                (oldV, newV) -> oldV));
        return objectMapper.convertValue(productPage, new TypeReference<ArrayList<Map<String, Object>>>() {});
    }

    /**
     * description 查询产品所有详细信息
     * @param product 产品ID
     * @return java.util.Map<java.lang.String, java.lang.Object>
     * @author qinzhenguan
     * @createDate 2023/11/29 13:19
     */
    @Override
    public Map<String, Object> queryDetailAll(Product product) {
        Map<String, Object> resultMap = new HashMap<>(200);
        BigDecimal productId =  product.getProductId();
        ProductDetail productDetail = detailRepository.findByProductId(productId);
        ProductTechnology productTechnology = technologyRepository.findByProductId(productId);

        // ---------------------------------  类别查询 ---------------------------------
        ArrayList<Brand> brands = (ArrayList<Brand>)brandRepository.findAll();
        ArrayList<Factory> factorys = (ArrayList<Factory>)factoryRepository.findAll();
        ArrayList<Color> colors = (ArrayList<Color>)colorRepository.findAll();
        ArrayList<ProductCategory> categorys = (ArrayList<ProductCategory>)categoryRepository.findAll();

        // ---------------------------------  详情查询 ---------------------------------

        ArrayList<ProductMaterial> material = (ArrayList<ProductMaterial>) materialRepository.findByProductId(productId);
        ArrayList<ProductLoss> loss = (ArrayList<ProductLoss>) lossRepository.findByProductId(productId);
        Set<Object> entitys = new HashSet<>();
        entitys.add(typesetConfRepository.findByProductId(productId));
        entitys.add(lookedRepository.findByProductId(productId));
        entitys.add(publishedConfRepository.findByProductId(productId));
        entitys.add(productDetail);
        entitys.add(productTechnology == null ? new ProductTechnology() : productTechnology);
        entitys.add(product);
        entitys.add(material);

        Map<String, Object> assemble = assembleResultOfForm(entitys);
        resultMap.put("rawData", assemble);
        assemble.put("productLoss", loss);

        // ---------------------------------  版面查询 ---------------------------------
        if(productDetail != null) {
            if(productDetail.getBackProductFaceId() != null) {
                ProductFace frontFace = faceRepository.findByProductFaceIdEquals(productDetail.getBackProductFaceId());
                ArrayList <ProductFaceColor> fronColors =
                        (ArrayList<ProductFaceColor>) faceColorRepository.findByProductFaceId(productDetail.getBackProductFaceId());
                assemble.put("frontFace", frontFace);
                assemble.put("fronColors", fronColors == null ? new ArrayList<>() : fronColors);
            } else {
                ProductFaceColor fc = new ProductFaceColor();
                fc.setProductFaceColorId("111");
                fc.setColorId("1");
                ArrayList<ProductFaceColor> fronColors =  new ArrayList<>();
                fronColors.add(fc);
                assemble.put("fronColors", fronColors);
            }
            if(productDetail.getBackProductFaceId() != null) {
                ProductFace backFace = faceRepository.findByProductFaceIdEquals(productDetail.getBackProductFaceId());
                ArrayList <ProductFaceColor> backColors =
                        (ArrayList<ProductFaceColor>) faceColorRepository.findByProductFaceId(productDetail.getBackProductFaceId());
                assemble.put("backFace", backFace);
                assemble.put("backColors", backColors == null ? new ArrayList<>() : backColors);
            } else {
                ProductFaceColor fc = new ProductFaceColor();
                fc.setProductFaceColorId("111");
                fc.setColorId("1");
                ArrayList<ProductFaceColor> fronColors =  new ArrayList<>();
                fronColors.add(fc);
                assemble.put("backColors", fronColors);
            }

        }

        // ---------------------------------  工序查询 ---------------------------------
        if(productTechnology != null) {
            if(productTechnology.getProductTechnologyId() != null) {
                ArrayList<ProductProcess> processesed =
                        (ArrayList<ProductProcess>) processRepository.findByProductTechnologyId(productTechnology.getProductTechnologyId());
                assemble.put("processed", processesed == null ? new ArrayList<>() : processesed);
            } else {
                ProductProcess fc = new ProductProcess();
                fc.setProductProcessId("111");
                fc.setProductProcessId("1");
                ArrayList<ProductProcess> processed =  new ArrayList<>();
                processed.add(fc);
                assemble.put("processed", processed);
            }
        }

        HashMap<String, Object> listMaps = new HashMap<>(100);
        listMaps.put("selectedBrand", objectMapper.convertValue(brands, List.class));
        listMaps.put("selectedFactory", objectMapper.convertValue(factorys, List.class));
        listMaps.put("selectedColor", objectMapper.convertValue(colors, List.class));
        listMaps.put("selectedCategory", objectMapper.convertValue(categorys, List.class));
        resultMap.put("listMaps", listMaps);
//        resultMap.put("selectedBrand", convertToMap(Brand.class, "brandName", "brandId", brands));
//        resultMap.put("selectedFactory", convertToMap(Factory.class, "factoryName", "factoryId", factorys));
//        resultMap.put("selectedColor", convertToMap(Color.class, "colorName", "colorId", colors));
//        resultMap.put("selectedCategory", convertToMap(ProductCategory.class, "productCategoryId",
//                "productCategoryName", categorys));
        return resultMap;
    }

    @Override
    @Transactional(transactionManager="transactionManager")
    public HashMap<String, Object> saveOrUpdataOnAllDetail(HashMap<String, Object> newData) {
        Product product = objectMapper.convertValue(newData.get("Product"), Product.class);
        ProductDetail productDetail = objectMapper.convertValue(newData.get("ProductDetail"), ProductDetail.class);
        ProductLooked productLooked = objectMapper.convertValue(newData.get("ProductLooked"), ProductLooked.class);

        TypedQuery<Product> query = entityManager.createQuery("SELECT s FROM Product s", Product.class);
        List<Product> products = query.getResultList();

        if(product != null) {
            BigDecimal id = product.getProductId();
            if(id == null) {
                product.setProductId(String.valueOf(Snowflake.nextId()));
                entityManager.persist(product);
            }
            entityManager.merge(product);
        }
        if(productDetail != null) {
            BigDecimal id = productDetail.getProductDetailId();
            if(id == null) {
                productDetail.setProductDetailId(String.valueOf(Snowflake.nextId()));
                entityManager.persist(productDetail);
            }
            entityManager.merge(productDetail);
        }
        if(productLooked != null) {
            BigDecimal id = productLooked.getProductLookedId();
            if(id == null) {
                productLooked.setProductLookedId(String.valueOf(Snowflake.nextId()));
                entityManager.persist(productLooked);
            }
            entityManager.merge(productLooked);
        }
//        String str = null;
//        str.length();
        entityManager.flush();

        return null;
    }

    /**
     * description 返回页面 Form 所需的数据格式
     * @param entitys   所有相关的 实体。 必须为 JavaBean 调用了 .getClass().getSimpleName()
     * @return java.util.HashMap<java.lang.String, java.lang.Object>
     * @author qinzhenguan
     * @createDate 2023/12/5 14:09
     */
    public Map<String, Object> assembleResultOfForm(Set<Object> entitys) {
        Map<String, Object> map = new HashMap<>(256);
        for(Object item : entitys) {
            if(item == null) {
                continue;
            }
            if(item instanceof List) {
                if(((List<?>) item).size() > 0) {
                    map.put((((List<?>) item).get(0)).getClass().getSimpleName(), objectMapper.convertValue(item, List.class));
                }
            } else {
                map.put(item.getClass().getSimpleName(), objectMapper.convertValue(item, new TypeReference<Map<? extends String, ?>>() {}) );
            }
            // 这里要获取所有的属性名， 保存对应关系， 属性： 所属实体类；
            //map.putAll(mapper.convertValue(item, new TypeReference<Map<? extends String, ?>>() {}));
        }
        return map;
    }

    /**
     * description
     * @param clazz Bean
     * @param displayProperty   需要显示的属性
     * @param valueProperty     连接的属性
     * @param list  需要转换的 List
     * @return java.util.Map<java.lang.Object, java.lang.Object>
     * @author qinzhenguan
     * @createDate 2023/12/1 16:09
     */
    public Map<Object, Object> convertToMap(Class<?> clazz, String displayProperty, String valueProperty,
                                            List<?> list) {
        Set<String> set = new HashSet<>();
        set.add(displayProperty);
        set.add(valueProperty);
        if(checkClassesAndProperties(clazz, set, list.size())) {
            
            LinkedList<Map<String, Object>> mapList = objectMapper.convertValue(list, new TypeReference<LinkedList<Map<String, Object>>>() {});
            return mapList.stream().collect(Collectors.toMap(map -> map.get(displayProperty), map -> map.get(valueProperty),
                    (oldV, newV) -> oldV));
        }
        return null;
    }

    /**
     * description  只检查传入属性是否合法
     * @param clazz Bean
     * @param set   需要检查的 Propertys
     * @param listSize  需要检查的 ListSize
     * @return java.lang.Boolean    true of false
     * @author qinzhenguan
     * @createDate 2023/12/1 15:58
     */
    public Boolean checkClassesAndProperties(Class<?> clazz, Set<String> set, int listSize) {
        if(listSize == 0) {
            System.err.println("[checkClassesAndProperties] :  List< "+clazz.getName()+" > 需要转换的列表没有元素。请检查~！");
            return false;
        }
        Field[] fields = clazz.getDeclaredFields();
        Method[] methods = clazz.getDeclaredMethods();
        for(String item : set) {
            Field field = Arrays.stream(fields).filter(f -> item.equals(f.getName())).findFirst().orElse(null);
            if(field == null) {
                System.err.println("[checkClassesAndProperties] : " + clazz.getName() + " 无法找到 ： " + item + " " +
                        "该属性。请检查~！");
                return false;
            }
            Method method = Arrays.stream(methods)
                    .filter(m -> m.getName().toLowerCase().contains("get" + item.toLowerCase())).findFirst().orElse(null);
            if (method == null) {
                System.err.println("[checkClassesAndProperties] : " + item + " 该属性无法找到构造方法。请检查~！");
                return false;
            }
        }
        return true;
    }

    /**
     * description 转换成 Map 不需要转换为实体
     * @param clazz Bean
     * @param propertyName 连接的 property
     * @param list  需要转换的 List
     * @return java.util.Map<java.lang.Object, java.lang.Object>
     * @author qinzhenguan
     * @createDate 2023/11/29 11:36
     */
    public Map<Object, Object> convertToMap(Class<?> clazz, String propertyName, List<?> list) {
        return convertToMap(clazz, propertyName, list, false);
    }

    /**
     * description 转换成 Map 保留映射
     * @param clazz Bean
     * @param propertyName  连接的 property
     * @param list  需要转换的 List
     * @param isBean    是否需要转换为实体 （当前应用不需要返回实体）
     * @return java.util.Map<java.lang.Object, java.lang.Object>
     * @author qinzhenguan
     * @createDate 2023/11/29 11:34
     */
    public Map<Object, Object> convertToMap(Class<?> clazz, String propertyName, List<?> list, boolean isBean) {
        Map<String, Object> checkMap = checkClassesAndProperties(clazz, propertyName, list.size());
        String ok = "isOk";
        if(!(Boolean) checkMap.get(ok)) {
            return null;
        }
        Method method = (Method) checkMap.get("method");
        
        LinkedList<Map<String, Object>> mapList = objectMapper.convertValue(list, new TypeReference<LinkedList<Map<String, Object>>>() {});
        Map<Object, Object> resultMap = new HashMap<>(10);
        Map<Object, Object> convert = new HashMap<>(1024);
        if(isBean) {
            // ---------------------------------  实体 ---------------------------------
            try {
                for (int i = 0; i < mapList.size(); i++) {
                    Object o = clazz.newInstance();
                    BeanUtils.populate(o, mapList.get(i));
                    Object key = method.invoke(o);
                    convert.put(key, o);
                }
            } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        } else {
            // ---------------------------------  非实体 ---------------------------------
            convert = mapList.stream().collect(Collectors.toMap(map -> map.get(propertyName), Function.identity(),
                    (oldV, newV) -> oldV));
            //此处仍需处理 Key == null;
        }
        resultMap.put("map", convert);
        resultMap.put("list", list);
        return resultMap;
    }

    /**
     * description 检查传入参数是否合法 并返回构造方法
     * 
     * @param clazz javaBean
     * @param propertyName property
     * @return java.util.HashMap<java.lang.String, java.lang.Object> { "isOk" : 是否通过检查, "method"：获取得到的 method }
     * @author qinzhenguan
     * @createDate 2023/11/29 11:05
     */
    public HashMap<String, Object> checkClassesAndProperties(Class<?> clazz, String propertyName, int listSize) {
        HashMap<String, Object> map = new HashMap<>(33);
        if(listSize == 0) {
            System.err.println("[checkClassesAndProperties] :  List< "+clazz.getName()+" > 需要转换的列表没有元素。请检查~！");
            map.put("isOk", false);
            return map;
        }
        Field[] fields = clazz.getDeclaredFields();
        Field field = Arrays.stream(fields).filter(f -> propertyName.equals(f.getName())).findFirst().orElse(null);
        if(field == null) {
            System.err.println("[checkClassesAndProperties] : " + clazz.getName() + " 无法找到 ： " + propertyName + " " +
                    "该属性。请检查~！");
            map.put("isOk", false);
            return map;
        }
        Method[] methods = clazz.getDeclaredMethods();
        Method method = Arrays.stream(methods)
                .filter(m -> m.getName().toLowerCase().contains("get" + propertyName.toLowerCase())).findFirst().orElse(null);
        if (method == null) {
            System.err.println("[checkClassesAndProperties] : " + propertyName + " 该属性无法找到构造方法。请检查~！");
            map.put("isOk", false);
            return map;
        }
        map.put("isOk", true);
        map.put("method", method);
        return map;
    }
}
