package com.eactive.eai.rms.onl.transaction.apim;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

/**
 * 생성/저장된 OpenAPI(3.0) spec JSON 에 인증 체계(components.securitySchemes + 글로벌 security)를 주입한다.
 *
 * <p>포털 {@code DjbSwaggerSpecEnricher} 와 동일 모델링:
 * 게이트웨이가 토큰/키를 표준 Authorization 이 아니라 커스텀 헤더로 받으므로 OAuth·API_KEY 모두
 * <b>apiKey-in-header</b> 스킴으로 표현(헤더명만 상이).
 *
 * <p>authtype(TSEAIHE01.AUTHTYPE, 소문자 저장) 매핑:
 * {@code oauth/oauth2/ca → OAUTH}, {@code api_key/apikey → API_KEY}, 그 외 → 주입 없음.
 * 원본 트리를 보존하며 조작한다(문자열 치환 금지). 매핑 불가/빈 스펙/파싱 실패 시 원본 유지(멱등).
 */
@Service
public class DjbApiSpecSwaggerEnricher {

    private final ObjectMapper objectMapper = new ObjectMapper();

    public static final String OAUTH_SCHEME   = "djbOAuth";
    public static final String API_KEY_SCHEME = "djbApiKey";
    // 게이트웨이 헤더명 (포털 DjbTestbedGatewayProperty 기본값과 동일)
    public static final String OAUTH_HEADER    = "X-AUTH-TOKEN";
    public static final String API_KEY_HEADER  = "X-DJB-API-Key";

    /** authtype → 스킴명. 매핑 불가면 null. */
    public String schemeNameOf(String authtype) {
        if (authtype == null) {
            return null;
        }
        switch (authtype.trim().toLowerCase()) {
            case "oauth":
            case "oauth2":
            case "ca":
                return OAUTH_SCHEME;
            case "api_key":
            case "apikey":
                return API_KEY_SCHEME;
            default:
                return null;
        }
    }

    /**
     * spec JSON 에 authtype 기반 securityScheme 주입. 매핑 불가/빈 스펙/파싱 실패 시 원본 그대로 반환.
     */
    public String enrich(String specJson, String authtype) {
        String schemeName = schemeNameOf(authtype);
        if (StringUtils.isBlank(specJson) || schemeName == null) {
            return specJson;
        }
        try {
            JsonNode parsed = objectMapper.readTree(specJson);
            if (!parsed.isObject()) {
                return specJson;
            }
            ObjectNode root = (ObjectNode) parsed;

            ObjectNode scheme = objectMapper.createObjectNode();
            scheme.put("type", "apiKey");
            scheme.put("in", "header");
            scheme.put("name", OAUTH_SCHEME.equals(schemeName) ? OAUTH_HEADER : API_KEY_HEADER);

            boolean swagger2 = root.has("swagger") && !root.has("openapi");
            if (swagger2) {
                getOrCreateObject(root, "securityDefinitions").set(schemeName, scheme);
            } else {
                ObjectNode components = getOrCreateObject(root, "components");
                getOrCreateObject(components, "securitySchemes").set(schemeName, scheme);
            }

            // 글로벌 security 요구사항 (apiKey 스킴은 빈 스코프 배열)
            ArrayNode security = objectMapper.createArrayNode();
            ObjectNode requirement = objectMapper.createObjectNode();
            requirement.set(schemeName, objectMapper.createArrayNode());
            security.add(requirement);
            root.set("security", security);

            return objectMapper.writeValueAsString(root);
        } catch (Exception e) {
            return specJson; // 파싱/직렬화 실패 → 원본 유지(멱등)
        }
    }

    private ObjectNode getOrCreateObject(ObjectNode parent, String field) {
        JsonNode node = parent.get(field);
        if (node != null && node.isObject()) {
            return (ObjectNode) node;
        }
        ObjectNode created = objectMapper.createObjectNode();
        parent.set(field, created);
        return created;
    }
}
