package com.zcloud.personnel.positioning.command.query;

import com.zcloud.personnel.positioning.persistence.dataobject.OrgLocalCorpDO;
import com.zcloud.personnel.positioning.persistence.dataobject.OrgLocalDepartmentDO;
import com.zcloud.personnel.positioning.persistence.repository.OrgMatchRepository;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

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

/**
 * Resolves FindS organization/department name snapshots against the local corp_info/department tree.
 *
 * <p>{@code department.parent_ids} is a materialized path shaped as {@code "0,1,<rootCorpId>,...,<selfId>"},
 * so the root company id for any department row is always the 3rd comma-separated segment.</p>
 */
@Component
@RequiredArgsConstructor
class OrgLocalLookupService {
    private static final int BATCH_SIZE = 500;
    private final OrgMatchRepository orgMatchRepository;

    /**
     * {@code corp_info.corpinfo_id} is not a foreign key into {@code department}: only the single
     * platform-tenant root department happens to share its id with its corp_info row. Every other
     * company is represented in {@code department} as its own node (a distinct bigint id) with
     * {@code dept_type_enum = 'TENANT'} and the same name as its corp_info entry, so that node's id
     * is looked up here and used as the starting point for descending into its sub-departments.
     * When a company name is not uniquely identifiable among TENANT-type department nodes, no
     * department id is returned and callers must stop at the corp-level match.
     */
    Map<String, List<LocalCorp>> findCorpsByNames(Set<String> corpNames) {
        Map<String, List<LocalCorp>> grouped = new LinkedHashMap<>();
        for (List<String> batch : batches(corpNames)) {
            for (OrgLocalCorpDO row : orgMatchRepository.listCorpsByNames(batch)) {
                LocalCorp corp = new LocalCorp();
                corp.setCorpinfoId(row.getCorpinfoId());
                corp.setCorpinfoName(row.getCorpinfoName());
                corp.setCreditCode(row.getCreditCode());
                corp.setCorpDepartmentId(row.getCorpDepartmentId());
                grouped.computeIfAbsent(corp.getCorpinfoName(), key -> new ArrayList<>()).add(corp);
            }
        }
        return grouped;
    }

    /**
     * Finds the single direct child department of {@code parentId} named {@code childName}. Used to
     * walk a FindS org path (root company, then successive department segments) one level at a time,
     * since FindS and the local platform do not always share the same hierarchy depth.
     */
    LocalDepartment findChildDepartment(String parentId, String childName) {
        if (!StringUtils.hasText(parentId) || !StringUtils.hasText(childName)) {
            return null;
        }
        OrgLocalDepartmentDO row = orgMatchRepository.findChildDepartment(parentId, childName);
        if (row == null) {
            return null;
        }
        LocalDepartment department = new LocalDepartment();
        department.setDepartmentId(row.getDepartmentId());
        department.setDepartmentName(row.getDepartmentName());
        department.setRootCorpinfoId(row.getRootCorpinfoId());
        department.setRootCorpinfoName(row.getRootCorpinfoName());
        return department;
    }

    private List<List<String>> batches(Set<String> values) {
        List<String> normalized = new ArrayList<>();
        if (values != null) {
            for (String value : values) {
                String item = value == null ? null : value.trim();
                if (StringUtils.hasText(item)) {
                    normalized.add(item);
                }
            }
        }
        List<List<String>> result = new ArrayList<>();
        for (int from = 0; from < normalized.size(); from += BATCH_SIZE) {
            result.add(normalized.subList(from, Math.min(from + BATCH_SIZE, normalized.size())));
        }
        return result;
    }

    @Data
    static class LocalCorp {
        private String corpinfoId;
        private String corpinfoName;
        private String creditCode;
        private Long corpDepartmentId;
    }

    @Data
    static class LocalDepartment {
        private Long departmentId;
        private String departmentName;
        private String rootCorpinfoId;
        private String rootCorpinfoName;
    }
}
