package com.izouma.nineth.service;

import com.alibaba.fastjson.JSON;
import com.github.kevinsawicki.http.HttpRequest;
import com.izouma.nineth.domain.District;
import com.izouma.nineth.enums.DistrictLevel;
import com.izouma.nineth.repo.DistrictRepo;
import com.izouma.nineth.utils.amap.DistrictsItem;
import com.izouma.nineth.utils.amap.QueryDistrictResponse;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class DistrictService {

    private final DistrictRepo districtRepo;

    public List<District> get(DistrictLevel level, DistrictLevel maxLevel, Long parent) {
        return districtRepo.findAll((Specification<District>) (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (level != null) {
                predicates.add(criteriaBuilder.equal(root.get("level"), level));
            }
            if (parent != null) {
                predicates.add(criteriaBuilder.equal(root.get("parent"), parent));
            }
            if (maxLevel != null) {
                List<DistrictLevel> list = Arrays.stream(DistrictLevel.values())
                        .filter(l -> l.getValue() <= maxLevel.getValue())
                        .collect(Collectors.toList());
                if (list.isEmpty()) {
                    predicates.add(criteriaBuilder.equal(root.get("level"), DistrictLevel.NONE));
                } else {
                    predicates.add(root.get("level").in(list));
                }
            }
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        }).stream().peek(district -> {
            if (maxLevel != null) {
                int childCount = 0;
                switch (maxLevel) {
                    case PROVINCE:
                        break;
                    case CITY:
                        childCount = district.getCityCount();
                        break;
                    case DISTRICT:
                        childCount = district.getCityCount() + district.getDistrictCount();
                        break;
                    case STREET:
                        childCount = district.getCityCount() + district.getDistrictCount() + district.getStreetCount();
                        break;
                }
                district.setLeaf(childCount == 0);
            } else {
                district.setLeaf(district.getChildCount() == 0);
            }
        }).collect(Collectors.toList());
    }

    public void sync() {
        QueryDistrictResponse response = JSON.parseObject(HttpRequest.get("https://restapi.amap.com/v3/config/district?key=3d59fb422c5c13af59bf82a8b6f3ad54&subdistrict=4")
                .body(), QueryDistrictResponse.class);
        response.getDistricts().get(0).getDistricts().stream().parallel().forEach(item -> saveDistrict(item, null));
    }

    private void saveDistrict(DistrictsItem item, Long parentId) {
        District district = District.builder()
                .id(Long.parseLong(item.getAdcode()))
                .name(item.getName())
                .parent(parentId)
                .level(DistrictLevel.valueOf(item.getLevel().toUpperCase()))
                .build();
        if (district.getLevel() == DistrictLevel.STREET) {
            district.setId(Long.parseLong(district.getId() + String.format("%02d", item.getIdx())));
        }
        if (StringUtils.isNotBlank(item.getCenter())) {
            String[] arr = item.getCenter().split(",");
            district.setLng(Double.parseDouble(arr[0]));
            district.setLat(Double.parseDouble(arr[1]));
        }
        if (item.getCitycode() != null && item.getCitycode() instanceof String) {
            district.setCityCode((String) item.getCitycode());
        }
        if (item.getDistricts() != null) {
            district.setChildCount(item.getDistricts().size());
            district.setCityCount((int) item.getDistricts()
                    .stream()
                    .filter(d -> DistrictLevel.valueOf(d.getLevel().toUpperCase()) == DistrictLevel.CITY)
                    .count());
            district.setDistrictCount((int) item.getDistricts()
                    .stream()
                    .filter(d -> DistrictLevel.valueOf(d.getLevel().toUpperCase()) == DistrictLevel.DISTRICT)
                    .count());
            district.setStreetCount((int) item.getDistricts()
                    .stream()
                    .filter(d -> DistrictLevel.valueOf(d.getLevel().toUpperCase()) == DistrictLevel.STREET)
                    .count());
        } else {
            district.setChildCount(0);
        }
        districtRepo.save(district);
        if (item.getDistricts() != null) {
            int[] idx = {1};
            item.getDistricts().stream().parallel().forEach(child -> {
                child.setIdx(idx[0]++);
                saveDistrict(child, district.getId());
            });
        }
    }

}
