package com.zioinfo.kintex.admin;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zioinfo.kintex.admin.dto.RulesetSummaryDto;
import com.zioinfo.kintex.common.error.ApiException;
import com.zioinfo.kintex.common.error.ErrorCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * 관리자 룰셋 조회 서비스 — classpath {@code rulesets/*.json}(규정·요율)을 열거·파싱해 버전 이력과 규칙 전문을 제공한다.
 * 룰셋은 코드가 아닌 데이터로 버전 관리되므로(규정 개정 대응) DB 없이 리소스만 조회한다. 편집 없음(읽기 전용).
 */
@Service
public class AdminRulesetService {

    private static final Logger log = LoggerFactory.getLogger(AdminRulesetService.class);

    /** 파일명 화이트리스트(경로 트래버설 차단) — 열거된 리소스명과 이중 대조한다. */
    private static final Pattern SAFE_NAME = Pattern.compile("[A-Za-z0-9._-]+\\.json");

    private final ObjectMapper objectMapper;
    private final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

    public AdminRulesetService(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    /** 룰셋 파일 요약 목록(파일명·버전·규칙 수·모듈 분포). */
    public List<RulesetSummaryDto> list() {
        List<RulesetSummaryDto> out = new ArrayList<>();
        for (Resource r : enumerate()) {
            String name = r.getFilename();
            if (name == null) {
                continue;
            }
            JsonNode root = readTree(r, name);
            if (root == null) {
                continue;
            }
            out.add(summarize(name, root));
        }
        out.sort((a, b) -> b.name().compareTo(a.name())); // 최신 버전 파일이 위로
        return out;
    }

    /** 지정 룰셋 파일의 규칙 전문(JSON 원문 파싱 트리). 존재하지 않으면 404. */
    public JsonNode detail(String name) {
        if (name == null || !SAFE_NAME.matcher(name).matches()) {
            throw new ApiException(ErrorCode.VALIDATION, "룰셋 파일명이 유효하지 않습니다.");
        }
        for (Resource r : enumerate()) {
            if (name.equals(r.getFilename())) {
                JsonNode root = readTree(r, name);
                if (root == null) {
                    throw new ApiException(ErrorCode.NOT_FOUND, "룰셋을 읽을 수 없습니다.");
                }
                return root;
            }
        }
        throw new ApiException(ErrorCode.NOT_FOUND, "룰셋을 찾을 수 없습니다.");
    }

    private RulesetSummaryDto summarize(String name, JsonNode root) {
        String version = root.path("rulesetVersion").asText(name);
        String effectiveDate = root.hasNonNull("effectiveDate") ? root.get("effectiveDate").asText() : null;
        JsonNode rules = root.path("rules");
        int ruleCount = rules.isArray() ? rules.size() : 0;

        Map<String, Integer> dist = new LinkedHashMap<>();
        if (rules.isArray()) {
            for (JsonNode rule : rules) {
                JsonNode modules = rule.path("module");
                if (modules.isArray()) {
                    for (JsonNode m : modules) {
                        dist.merge(m.asText(), 1, Integer::sum);
                    }
                }
            }
        }
        return new RulesetSummaryDto(name, version, effectiveDate, ruleCount, dist);
    }

    private Resource[] enumerate() {
        try {
            return resolver.getResources("classpath:rulesets/*.json");
        } catch (IOException e) {
            log.error("룰셋 리소스 열거 실패: {}", e.getMessage());
            return new Resource[0];
        }
    }

    private JsonNode readTree(Resource r, String name) {
        try (InputStream in = r.getInputStream()) {
            return objectMapper.readTree(in);
        } catch (IOException e) {
            log.error("룰셋 파싱 실패({}): {}", name, e.getMessage());
            return null;
        }
    }
}
