package com.ecep.contract.service;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

import com.ecep.contract.PageArgument;
import com.ecep.contract.WebSocketService;
import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.msg.SimpleMessage;
import com.ecep.contract.vm.IdentityViewModel;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

public class QueryService<T extends IdentityEntity, TV extends IdentityViewModel<T>>
        implements ViewModelService<T, TV> {
    @Autowired
    protected WebSocketService webSocketService;
    @Autowired
    protected ObjectMapper objectMapper;
    private long readTimeout = 30000;

    @SuppressWarnings("unchecked")
    public TV createNewViewModel() {
        try {
            Type genericSuperclass = getClass().getGenericSuperclass();
            String typeName = genericSuperclass.getTypeName();
            // System.out.println("typeName = " + typeName);
            String clz = typeName.split("<")[1].split(">")[0].split(",")[1].trim();
            // System.out.println("clz = " + clz);
            Class<?> clazz = Class.forName(clz);
            return (TV) clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("无法创建ViewModel实例", e);
        }
    }

    @SuppressWarnings("unchecked")
    public T createNewEntity() {
        try {
            Type genericSuperclass = getClass().getGenericSuperclass();
            String typeName = genericSuperclass.getTypeName();
            // System.out.println("typeName = " + typeName);
            String clz = typeName.split("<")[1].split(">")[0].split(",")[0].trim();
            // System.out.println("clz = " + clz);
            Class<?> clazz = Class.forName(clz);
            return (T) clazz.getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            throw new RuntimeException("无法创建Entity实例", e);
        }
    }

    @Override
    public T save(T entity) {
        SimpleMessage msg = new SimpleMessage();
        msg.setService(getBeanName());
        msg.setMethod("save");
        msg.setArguments(entity);
        try {
            Object response = webSocketService.send(msg).get(readTimeout, TimeUnit.MILLISECONDS);
            if (response != null) {
                objectMapper.updateValue(entity, response);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return entity;
    }

    @Override
    public void delete(T entity) {
        SimpleMessage msg = new SimpleMessage();
        msg.setService(getBeanName());
        msg.setMethod("delete");
        msg.setArguments(entity);
        try {
            JsonNode response = webSocketService.send(msg).get(readTimeout, TimeUnit.MILLISECONDS);
            if (response != null) {
                objectMapper.updateValue(entity, response);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public CompletableFuture<T> asyncFindById(Integer id) {
        SimpleMessage msg = new SimpleMessage();
        msg.setService(getBeanName());
        msg.setMethod("findById");
        msg.setArguments(id);
        return webSocketService.send(msg).orTimeout(readTimeout, TimeUnit.MILLISECONDS).handle((response, ex) -> {
            if (ex != null) {
                return null;
            }
            if (response == null) {

                return null;
            }
            T newEntity = createNewEntity();
            try {
                objectMapper.updateValue(newEntity, response);
            } catch (JsonMappingException e) {
                throw new RuntimeException(response.toString(), e);
            }
            return newEntity;
        });
    }

    @Override
    public T findById(Integer id) {
        try {
            return asyncFindById(id).get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public List<T> findAll() {
        return findAll(null, Pageable.unpaged()).getContent();
    }

    @Override
    public Page<T> findAll(Map<String, Object> params, Pageable pageable) {
        SimpleMessage msg = new SimpleMessage();
        msg.setService(getBeanName());
        msg.setMethod("findAll");
        msg.setArguments(params, PageArgument.of(pageable));
        try {
            JsonNode response = webSocketService.send(msg).get(readTimeout, TimeUnit.MILLISECONDS);
            if (response != null) {

                List<T> content = new ArrayList<>();
                JsonNode contentNode = response.get("content");
                if (contentNode != null && contentNode.isArray()) {
                    for (JsonNode node : contentNode) {
                        T newEntity = createNewEntity();
                        objectMapper.updateValue(newEntity, node);
                        content.add(newEntity);
                    }
                }

                JsonNode pageNode = response.get("page");

                int total = pageNode.get("totalElements").asInt();
                int totalPages = pageNode.get("totalPages").asInt();
                int size = pageNode.get("size").asInt();
                int number = pageNode.get("number").asInt();

                PageRequest newPageable = PageRequest.of(number, size);
                return new PageImpl<>(content, newPageable, total);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    public List<T> search(String searchText) {
        Map<String, Object> params = getSpecification(searchText);
        List<T> list = findAll(params, Pageable.ofSize(10)).getContent();
        return list;
    }
}
