package com.zioinfo.kintex.system.dept;

import com.zioinfo.kintex.common.error.ApiException;
import com.zioinfo.kintex.common.error.ErrorCode;
import com.zioinfo.kintex.tenant.TenantContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/** 조직 — 부서 트리 서비스. 테넌트 컨텍스트로 스코프(§8-2). */
@Service
public class DeptService {

    private final DeptMapper mapper;

    public DeptService(DeptMapper mapper) {
        this.mapper = mapper;
    }

    /** 전체 부서를 트리로 조립(루트=parentId null 또는 미존재 부모). */
    public List<DeptDto> tree() {
        String tenantId = TenantContext.currentTenantId();
        List<Map<String, Object>> rows = mapper.findAll(tenantId);
        Map<String, List<DeptDto>> childrenOf = new LinkedHashMap<>();
        Map<String, Map<String, Object>> byId = new LinkedHashMap<>();
        for (Map<String, Object> r : rows) {
            byId.put(str(r.get("id")), r);
        }
        List<DeptDto> roots = new ArrayList<>();
        // 재귀 없이 childrenOf 맵으로 조립(rows 순서=sort_order,id → 안정 정렬).
        for (Map<String, Object> r : rows) {
            String id = str(r.get("id"));
            String parentId = str(r.get("parentId"));
            List<DeptDto> kids = childrenOf.computeIfAbsent(id, k -> new ArrayList<>());
            DeptDto node = new DeptDto(id, str(r.get("deptName")), parentId,
                    intv(r.get("sortOrder")), str(r.get("useYn")), kids);
            if (parentId == null || !byId.containsKey(parentId)) {
                roots.add(node);
            } else {
                childrenOf.computeIfAbsent(parentId, k -> new ArrayList<>()).add(node);
            }
        }
        return roots;
    }

    @Transactional
    public void save(DeptSaveRequest req) {
        Map<String, Object> p = new java.util.HashMap<>();
        String id = (req.id() == null || req.id().isBlank())
                ? "dept-" + UUID.randomUUID().toString().substring(0, 12) : req.id();
        if (id.equals(req.parentId())) {
            throw new ApiException(ErrorCode.VALIDATION, "부모 부서를 자기 자신으로 지정할 수 없습니다.");
        }
        p.put("tenantId", TenantContext.currentTenantId());
        p.put("id", id);
        p.put("deptName", req.deptName());
        p.put("parentId", (req.parentId() == null || req.parentId().isBlank()) ? null : req.parentId());
        p.put("sortOrder", req.sortOrder());
        p.put("useYn", req.useYn());
        mapper.upsert(p);
    }

    @Transactional
    public void delete(String id) {
        String tenantId = TenantContext.currentTenantId();
        if (mapper.countChildren(tenantId, id) > 0) {
            throw new ApiException(ErrorCode.CONFLICT, "하위 부서가 있어 삭제할 수 없습니다.");
        }
        if (mapper.delete(tenantId, id) == 0) {
            throw new ApiException(ErrorCode.NOT_FOUND);
        }
    }

    private static String str(Object o) { return o == null ? null : String.valueOf(o); }
    private static Integer intv(Object o) {
        if (o instanceof Number n) return n.intValue();
        return o == null ? 0 : Integer.parseInt(String.valueOf(o));
    }
}
