package com.malk.benteler.service;

import com.malk.benteler.dto.EiamOrganizationalUnitOption;
import com.malk.server.common.McException;
import com.malk.server.eiam.EiamConf;
import com.malk.server.eiam.EiamOrganizationalUnit;
import com.malk.server.eiam.EiamOrganizationalUnitPage;
import com.malk.service.eiam.EiamClient_OrganizationalUnit;
import com.malk.service.eiam.EiamService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Queue;
import java.util.Set;

/**
 * 本特勒 EIAM 组织树查询编排。
 */
@Slf4j
@Service
public class EiamOrganizationalUnitLocalService {

    private static final String ROOT_ID = "ou_root";
    private static final int PAGE_SIZE = 100;
    private static final String CACHE_REFRESH_CRON = "0 0 8-19 * * MON-FRI";
    private static final String CACHE_REFRESH_ZONE = "Asia/Shanghai";

    private final EiamService eiamService;
    private final EiamClient_OrganizationalUnit organizationalUnitClient;
    private final EiamConf eiamConf;
    private volatile List<EiamOrganizationalUnitOption> organizationalUnitCache =
            Collections.emptyList();
    private volatile boolean cacheLoaded;

    public EiamOrganizationalUnitLocalService(EiamService eiamService,
                                              EiamClient_OrganizationalUnit organizationalUnitClient,
                                              EiamConf eiamConf) {
        this.eiamService = eiamService;
        this.organizationalUnitClient = organizationalUnitClient;
        this.eiamConf = eiamConf;
    }

    /**
     * 按当前部门名称包含匹配缓存中的组织树。
     *
     * @param name 部门名称关键字
     * @return 部门选择项，名称格式为“当前部门 (父部门)”
     */
    public List<EiamOrganizationalUnitOption> searchByName(String name) {
        if (StringUtils.isBlank(name)) {
            throw new McException("EIAM_ORG_NAME_EMPTY", "部门名称不能为空");
        }
        List<EiamOrganizationalUnitOption> organizationUnits = getOrganizationUnits();
        List<EiamOrganizationalUnitOption> matches = new ArrayList<>();
        for (EiamOrganizationalUnitOption option : organizationUnits) {
            if (containsIgnoreCase(currentName(option.getOrganizationalUnitName()), name)) {
                matches.add(option);
            }
        }
        matches.sort(Comparator.comparing(EiamOrganizationalUnitOption::getOrganizationalUnitName,
                Comparator.nullsLast(String::compareToIgnoreCase)));
        return matches;
    }

    /**
     * 北京时间工作日 08:00 至 19:00 每小时整点刷新 EIAM 部门缓存。
     */
    @Scheduled(cron = CACHE_REFRESH_CRON, zone = CACHE_REFRESH_ZONE)
    public void refreshOrganizationUnitCache() {
        try {
            synchronized (this) {
                replaceOrganizationUnitCache(loadOrganizationUnits());
            }
        } catch (RuntimeException exception) {
            // fixme 刷新失败时保留上一版缓存，避免连接器查询被短暂的 EIAM 异常影响。
            log.error("刷新 EIAM 部门缓存失败", exception);
        }
    }

    private List<EiamOrganizationalUnitOption> getOrganizationUnits() {
        if (cacheLoaded) {
            return organizationalUnitCache;
        }
        synchronized (this) {
            if (!cacheLoaded) {
                replaceOrganizationUnitCache(loadOrganizationUnits());
            }
            return organizationalUnitCache;
        }
    }

    private void replaceOrganizationUnitCache(
            List<EiamOrganizationalUnitOption> organizationUnits) {
        organizationalUnitCache = Collections.unmodifiableList(
                new ArrayList<>(organizationUnits));
        cacheLoaded = true;
        log.info("EIAM 部门缓存刷新完成，共 {} 个部门", organizationalUnitCache.size());
    }

    private List<EiamOrganizationalUnitOption> loadOrganizationUnits() {
        String token = eiamService.getAccessToken();
        List<EiamOrganizationalUnitOption> organizationUnits = new ArrayList<>();
        Queue<OrganizationalUnitNode> queue = new ArrayDeque<>();
        Set<String> visited = new HashSet<>();
        queue.add(new OrganizationalUnitNode(ROOT_ID, null));
        while (!queue.isEmpty()) {
            OrganizationalUnitNode parent = queue.poll();
            if (!visited.add(parent.getId())) {
                continue;
            }
            List<EiamOrganizationalUnit> children = listAllChildren(token, parent.getId());
            for (EiamOrganizationalUnit child : children) {
                if (child == null || StringUtils.isBlank(child.getOrganizationalUnitId())) {
                    continue;
                }
                queue.add(new OrganizationalUnitNode(child.getOrganizationalUnitId(),
                        child.getOrganizationalUnitName()));
                organizationUnits.add(EiamOrganizationalUnitOption.builder()
                        .organizationalUnitId(child.getOrganizationalUnitId())
                        .organizationalUnitName(displayName(child.getOrganizationalUnitName(),
                                parent.getName()))
                        .build());
            }
        }
        return organizationUnits;
    }

    /**
     * 按当前部门名称精确解析唯一的 EIAM 组织 ID。
     *
     * @param name 当前部门名称
     * @return EIAM 组织 ID
     */
    public String resolveUniqueId(String name) {
        List<EiamOrganizationalUnitOption> options = searchByName(name);
        List<EiamOrganizationalUnitOption> exact = new ArrayList<>();
        for (EiamOrganizationalUnitOption option : options) {
            String currentName = currentName(option.getOrganizationalUnitName());
            if (StringUtils.equalsIgnoreCase(StringUtils.trim(currentName), StringUtils.trim(name))) {
                exact.add(option);
            }
        }
        if (exact.size() != 1) {
            throw new McException("EIAM_ORG_NOT_UNIQUE",
                    "无法按名称唯一匹配 EIAM 部门: " + name);
        }
        return exact.get(0).getOrganizationalUnitId();
    }

    private List<EiamOrganizationalUnit> listAllChildren(String token, String parentId) {
        List<EiamOrganizationalUnit> all = new ArrayList<>();
        int pageNumber = 1;
        while (true) {
            EiamOrganizationalUnitPage page = organizationalUnitClient.listOrganizationalUnits(
                    token, eiamConf.getInstanceId(), eiamConf.getApplicationId(), parentId,
                    pageNumber, PAGE_SIZE);
            if (page == null) {
                throw new McException("EIAM_ORG_EMPTY_RESPONSE", "EIAM 部门列表响应为空");
            }
            List<EiamOrganizationalUnit> data = page.getData();
            if (data != null) {
                all.addAll(data);
            }
            int totalCount = page.getTotalCount() == null ? all.size() : page.getTotalCount();
            if (all.size() >= totalCount || data == null || data.isEmpty()) {
                return all;
            }
            pageNumber++;
        }
    }

    private boolean containsIgnoreCase(String value, String keyword) {
        return StringUtils.containsIgnoreCase(StringUtils.defaultString(value),
                StringUtils.trim(keyword));
    }

    private String displayName(String currentName, String parentName) {
        // prd 同名部门显示格式统一为“当前部门名称 (父部门名称)”，ID 保持不变。
        return StringUtils.isBlank(parentName)
                ? currentName : currentName + " (" + parentName + ")";
    }

    private String currentName(String displayName) {
        String value = StringUtils.defaultString(displayName);
        int parentStart = StringUtils.lastIndexOf(value, " (");
        return parentStart >= 0 && StringUtils.endsWith(value, ")")
                ? StringUtils.substring(value, 0, parentStart) : value;
    }

    private static class OrganizationalUnitNode {

        private final String id;
        private final String name;

        private OrganizationalUnitNode(String id, String name) {
            this.id = id;
            this.name = name;
        }

        private String getId() {
            return id;
        }

        private String getName() {
            return name;
        }
    }
}
