package org.qinan.safetyeval.app.executor;

import org.qinan.safetyeval.client.api.OrgDepartmentApi;
import org.qinan.safetyeval.client.dto.PageResponse;
import org.qinan.safetyeval.client.dto.SingleResponse;
import org.qinan.safetyeval.client.co.OrgDepartmentCO;
import org.qinan.safetyeval.client.dto.OrgDepartmentAddCmd;
import org.qinan.safetyeval.client.dto.OrgDepartmentModifyCmd;
import org.qinan.safetyeval.client.dto.OrgDepartmentPageQuery;
import org.qinan.safetyeval.domain.entity.OrgDepartmentEntity;
import org.qinan.safetyeval.domain.query.OrgDepartmentQuery;
import org.qinan.safetyeval.domain.query.PageResult;
import org.qinan.safetyeval.domain.service.OrgDepartmentDomainService;
import org.qinan.safetyeval.infrastructure.adapter.ThreadLocalUserInfoAdapter;
import org.qinan.safetyeval.infrastructure.adapter.auth.AuthUserContextAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 部门执行器（App层）
 *
 * @author safety-eval
 */
@Service
public class OrgDepartmentExecutor implements OrgDepartmentApi {

    @Lazy
    @Autowired(required = false)
    private OrgDepartmentDomainService orgDepartmentDomainService;

    @Override
    public SingleResponse<OrgDepartmentCO> add(OrgDepartmentAddCmd cmd) {
        OrgDepartmentEntity entity = new OrgDepartmentEntity();
        entity.setParentId(cmd.getParentId());
        entity.setDeptName(cmd.getDeptName());
        entity.setManagerName(cmd.getManagerName());
        entity.setManagerAccount(cmd.getManagerAccount());
        entity.setDeptLevelCode(cmd.getDeptLevelCode());
        entity.setDeptLevelName(cmd.getDeptLevelName());

        OrgDepartmentEntity result = orgDepartmentDomainService.add(entity);
        return SingleResponse.success(toCO(result));
    }

    @Override
    public SingleResponse<OrgDepartmentCO> get(Long id) {
        OrgDepartmentEntity entity = orgDepartmentDomainService.get(id);
        return SingleResponse.success(toCO(entity));
    }

    @Override
    public SingleResponse<OrgDepartmentCO> modify(OrgDepartmentModifyCmd cmd) {
        OrgDepartmentEntity entity = new OrgDepartmentEntity();
        entity.setId(cmd.getId() != null ? Long.parseLong(cmd.getId()) : null);
        entity.setParentId(cmd.getParentId() != null ? Long.parseLong(cmd.getParentId()) : null);
        entity.setDeptName(cmd.getDeptName());
        entity.setManagerName(cmd.getManagerName());
        entity.setManagerAccount(cmd.getManagerAccount());
        entity.setDeptLevelCode(cmd.getDeptLevelCode());
        entity.setDeptLevelName(cmd.getDeptLevelName());

        OrgDepartmentEntity result = orgDepartmentDomainService.modify(entity);
        return SingleResponse.success(toCO(result));
    }

    @Override
    public SingleResponse<Void> delete(Long id) {
        orgDepartmentDomainService.delete(id);
        return SingleResponse.success();
    }

    @Override
    public PageResponse<OrgDepartmentCO> page(OrgDepartmentPageQuery query) {
        OrgDepartmentQuery domainQuery = new OrgDepartmentQuery();
        domainQuery.setPageNum(query.getCurrent());
        domainQuery.setPageSize(query.getSize());
        domainQuery.setTenantId(AuthUserContextAdapter.getCurrentTenantId());
        domainQuery.setOrgId(ThreadLocalUserInfoAdapter.getOrgId());
        domainQuery.setDeptName(query.getDeptName());

        PageResult<OrgDepartmentEntity> pageResult = orgDepartmentDomainService.page(domainQuery);

        return PageResponse.of(
                pageResult.getRecords().stream()
                        .map(this::toCO)
                        .collect(java.util.stream.Collectors.toList()),
                pageResult.getTotal());
    }

    @Override
    public SingleResponse<List<OrgDepartmentCO>> tree(OrgDepartmentPageQuery query) {
        OrgDepartmentQuery domainQuery = new OrgDepartmentQuery();
        domainQuery.setTenantId(AuthUserContextAdapter.getCurrentTenantId());
        domainQuery.setOrgId(query.getOrgId() == null ? ThreadLocalUserInfoAdapter.get() : query.getOrgId());
        domainQuery.setDeptName(query.getDeptName());

        List<OrgDepartmentEntity> matched = orgDepartmentDomainService.list(domainQuery);
        Map<Long, OrgDepartmentEntity> nodeMap = new LinkedHashMap<>();
        for (OrgDepartmentEntity entity : matched) {
            collectWithAncestors(entity, nodeMap);
        }
        return SingleResponse.success(buildTree(nodeMap.values()));
    }

    private void collectWithAncestors(OrgDepartmentEntity entity, Map<Long, OrgDepartmentEntity> nodeMap) {
        OrgDepartmentEntity current = entity;
        while (current != null && current.getId() != null) {
            if (nodeMap.containsKey(current.getId())) {
                return;
            }
            nodeMap.put(current.getId(), current);
            Long parentId = current.getParentId();
            if (parentId == null || parentId == 0L) {
                return;
            }
            current = orgDepartmentDomainService.get(parentId);
        }
    }

    private List<OrgDepartmentCO> buildTree(Collection<OrgDepartmentEntity> entities) {
        Map<String, OrgDepartmentCO> idToCo = new LinkedHashMap<>();
        for (OrgDepartmentEntity entity : entities) {
            idToCo.put(String.valueOf(entity.getId()), toCO(entity));
        }

        List<OrgDepartmentCO> roots = new ArrayList<>();
        for (OrgDepartmentCO co : idToCo.values()) {
            String parentId = co.getParentId();
            if (parentId == null || "0".equals(parentId) || !idToCo.containsKey(parentId)) {
                roots.add(co);
                continue;
            }
            OrgDepartmentCO parent = idToCo.get(parentId);
            if (parent.getChildren() == null) {
                parent.setChildren(new ArrayList<>());
            }
            parent.getChildren().add(co);
        }
        return roots;
    }

    private OrgDepartmentCO toCO(OrgDepartmentEntity entity) {
        if (entity == null) {
            return null;
        }
        OrgDepartmentCO co = new OrgDepartmentCO();
        co.setId(entity.getId() != null ? String.valueOf(entity.getId()) : null);
        co.setParentId(entity.getParentId() != null ? String.valueOf(entity.getParentId()) : null);
        co.setDeptName(entity.getDeptName());
        co.setManagerName(entity.getManagerName());
        co.setManagerAccount(entity.getManagerAccount());
        co.setDeptLevelCode(entity.getDeptLevelCode());
        co.setDeptLevelName(entity.getDeptLevelName());
        co.setTenantId(entity.getTenantId() != null ? String.valueOf(entity.getTenantId()) : null);
        return co;
    }
}
