package go.kr.project.api.internal.client;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import go.kr.project.api.config.properties.VmisProperties;
import go.kr.project.api.internal.gpki.GpkiService;
import go.kr.project.api.internal.util.TxIdUtil;
import go.kr.project.api.model.Envelope;
import go.kr.project.api.model.request.BasicRequest;
import go.kr.project.api.model.request.LedgerRequest;
import go.kr.project.api.model.response.BasicResponse;
import go.kr.project.api.model.response.LedgerResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;

import java.nio.charset.StandardCharsets;

/**
 * 정부 시스템 API 클라이언트
 *
 * <p>이 클래스는 시군구연계 자동차 정보 조회를 위해 정부 시스템의 API를 호출하는
 * 클라이언트 역할을 수행합니다. HTTP 통신, 암호화, 에러 처리 등 정부 API와의
 * 모든 상호작용을 캡슐화합니다.</p>
 *
 * <h3>주요 책임:</h3>
 * <ul>
 *   <li>정부 API 엔드포인트로 HTTP 요청 전송</li>
 *   <li>GPKI(행정전자서명) 암호화/복호화 처리</li>
 *   <li>필수 HTTP 헤더 구성 및 관리</li>
 *   <li>요청/응답 데이터의 JSON 직렬화/역직렬화</li>
 *   <li>트랜잭션 ID(tx_id) 생성 및 추적</li>
 *   <li>네트워크 오류 및 HTTP 에러 처리</li>
 *   <li>상세한 로깅을 통한 디버깅 지원</li>
 * </ul>
 *
 * <h3>아키텍처 패턴:</h3>
 * <ul>
 *   <li>Adapter 패턴: 외부 정부 시스템 API를 내부 인터페이스로 변환</li>
 *   <li>Template Method 패턴: callModel 메서드가 공통 흐름을 정의</li>
 *   <li>Dependency Injection: 생성자를 통한 의존성 주입</li>
 * </ul>
 *
 * <h3>보안 특성:</h3>
 * <ul>
 *   <li>GPKI 암호화를 통한 데이터 보안 (선택적 활성화)</li>
 *   <li>API 키 기반 인증</li>
 *   <li>기관 식별 정보(INFO_SYS_ID, REGION_CODE 등)를 헤더에 포함</li>
 * </ul>
 *
 * @see RestTemplate
 * @see GpkiService
 * @see VmisProperties
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class GovernmentApiClient implements GovernmentApi {

    /**
     * Spring RestTemplate (통합 RestTemplate 사용)
     *
     * <p>HTTP 클라이언트로서 실제 네트워크 통신을 수행합니다.
     * 이 객체는 Spring Bean으로 주입되며, 설정에 따라 다음을 포함할 수 있습니다:</p>
     * <ul>
     *   <li>Connection Timeout 설정</li>
     *   <li>Read Timeout 설정</li>
     *   <li>Connection Pool 관리</li>
     *   <li>Rate Limiting (초당 요청 수 제한)</li>
     *   <li>메시지 컨버터 (Jackson for JSON)</li>
     *   <li>인터셉터 (로깅, 헤더 추가 등)</li>
     * </ul>
     */
    private final RestTemplate restTemplate;

    /**
     * VMIS 설정 속성
     *
     * <p>application.yml 또는 application.properties에서 로드된 설정값들입니다.
     * 포함되는 주요 설정:</p>
     * <ul>
     *   <li>정부 API URL (호스트, 포트, 경로)</li>
     *   <li>API 키 및 인증 정보</li>
     *   <li>시스템 식별 정보 (INFO_SYS_ID, REGION_CODE 등)</li>
     *   <li>GPKI 설정 (인증서 서버 ID 등)</li>
     * </ul>
     */
    private final VmisProperties props;

    /**
     * GPKI(행정전자서명) 서비스
     *
     * <p>정부24 등 공공기관 간 통신에 사용되는 암호화 서비스입니다.
     * 주요 기능:</p>
     * <ul>
     *   <li>요청 데이터 암호화 (공개키 암호화)</li>
     *   <li>응답 데이터 복호화 (개인키 복호화)</li>
     *   <li>전자서명 생성 및 검증</li>
     *   <li>암호화 활성화 여부 확인</li>
     * </ul>
     *
     * <p>암호화가 비활성화된 경우 평문(Plain Text)으로 통신합니다.</p>
     */
    private final GpkiService gpkiService;

    /**
     * Jackson ObjectMapper
     *
     * <p>Java 객체와 JSON 문자열 간의 변환을 담당합니다.
     * 주요 역할:</p>
     * <ul>
     *   <li>요청 객체를 JSON 문자열로 직렬화 (Serialization)</li>
     *   <li>응답 JSON을 Java 객체로 역직렬화 (Deserialization)</li>
     *   <li>제네릭 타입 처리 (TypeReference 사용)</li>
     *   <li>날짜/시간 포맷 변환</li>
     *   <li>null 값 처리</li>
     * </ul>
     *
     * <p>Spring Boot가 자동 구성한 ObjectMapper를 주입받아 사용하므로
     * 전역 설정(날짜 포맷, 네이밍 전략 등)이 일관되게 적용됩니다.</p>
     */
    private final ObjectMapper objectMapper;


    /**
     * 서비스 타입 열거형
     *
     * <p>정부 API 서비스의 종류를 구분하는 열거형입니다.
     * 각 서비스 타입은 서로 다른 엔드포인트와 API 키를 가집니다.</p>
     *
     * <h4>서비스 타입:</h4>
     * <ul>
     *   <li>BASIC: 자동차 기본사항 조회 서비스
     *       <ul>
     *         <li>차량번호로 기본 정보(소유자, 차종, 용도 등) 조회</li>
     *         <li>비교적 간단한 정보 제공</li>
     *         <li>응답 속도가 빠름</li>
     *       </ul>
     *   </li>
     *   <li>LEDGER: 자동차 등록원부(갑) 조회 서비스
     *       <ul>
     *         <li>상세한 등록 정보 및 법적 권리관계 조회</li>
     *         <li>저당권, 압류, 소유권 이전 이력 등 포함</li>
     *         <li>민감 정보를 포함하여 권한 검증이 엄격함</li>
     *       </ul>
     *   </li>
     * </ul>
     */
    public enum ServiceType {
        /**
         * Basic service type.
         */
        BASIC,
        /**
         * Ledger service type.
         */
        LEDGER }

    /**
     * HTTP 헤더 구성
     *
     * <p>정부 API 호출에 필요한 모든 HTTP 헤더를 구성하는 private 메서드입니다.
     * 정부 시스템은 엄격한 헤더 검증을 수행하므로 모든 필수 헤더가 정확히 포함되어야 합니다.</p>
     *
     * <h3>헤더 구성 항목:</h3>
     * <table border="1">
     *   <tr>
     *     <th>헤더명</th>
     *     <th>설명</th>
     *     <th>예시값</th>
     *     <th>필수여부</th>
     *   </tr>
     *   <tr>
     *     <td>Content-Type</td>
     *     <td>요청 바디의 미디어 타입 및 문자 인코딩</td>
     *     <td>application/json; charset=UTF-8</td>
     *     <td>필수</td>
     *   </tr>
     *   <tr>
     *     <td>Accept</td>
     *     <td>클라이언트가 수용 가능한 응답 형식</td>
     *     <td>application/json</td>
     *     <td>필수</td>
     *   </tr>
     *   <tr>
     *     <td>gpki_yn</td>
     *     <td>GPKI 암호화 사용 여부 (Y/N)</td>
     *     <td>Y</td>
     *     <td>필수</td>
     *   </tr>
     *   <tr>
     *     <td>tx_id</td>
     *     <td>트랜잭션 고유 ID (요청 추적용)</td>
     *     <td>20250104123045_abc123</td>
     *     <td>필수</td>
     *   </tr>
     *   <tr>
     *     <td>cert_server_id</td>
     *     <td>인증서 서버 식별자</td>
     *     <td>VMIS_SERVER_01</td>
     *     <td>필수</td>
     *   </tr>
     *   <tr>
     *     <td>api_key</td>
     *     <td>서비스별 API 인증 키</td>
     *     <td>abc123def456...</td>
     *     <td>필수</td>
     *   </tr>
     *   <tr>
     *     <td>cvmis_apikey</td>
     *     <td>CVMIS 시스템 API 키</td>
     *     <td>xyz789uvw012...</td>
     *     <td>필수</td>
     *   </tr>
     *   <tr>
     *     <td>INFO_SYS_ID</td>
     *     <td>정보시스템 식별자</td>
     *     <td>VMIS_SEOUL</td>
     *     <td>필수</td>
     *   </tr>
     * </table>
     *
     * <h3>문자 인코딩:</h3>
     * <ul>
     *   <li>Content-Type에 UTF-8 인코딩을 명시적으로 지정</li>
     *   <li>한글 데이터 처리를 위해 필수</li>
     *   <li>정부 시스템이 다양한 클라이언트와 호환되도록 표준 인코딩 사용</li>
     * </ul>
     *
     * <h3>보안 고려사항:</h3>
     * <ul>
     *   <li>API 키는 설정 파일에서 안전하게 관리</li>
     *   <li>로그에 API 키가 노출되지 않도록 주의</li>
     *   <li>각 서비스(BASIC, LEDGER)마다 별도의 API 키 사용 가능</li>
     * </ul>
     *
     * @param svc 서비스 설정 객체 (API 키, 경로 등 포함)
     * @param txId 이번 요청의 트랜잭션 ID
     * @return HttpHeaders 구성된 HTTP 헤더 객체
     */
    private HttpHeaders buildHeaders(VmisProperties.GovProps.Service svc, String txId) {
        // 1. 빈 HttpHeaders 객체 생성
        HttpHeaders headers = new HttpHeaders();

        // 2. Content-Type 설정
        // UTF-8 인코딩을 명시하여 한글 데이터가 올바르게 전송되도록 함
        headers.setContentType(new MediaType("application", "json", StandardCharsets.UTF_8));

        // 3. Accept 헤더 설정
        // 서버에게 JSON 형식의 응답을 요청함
        headers.setAccept(java.util.Collections.singletonList(MediaType.APPLICATION_JSON));

        // 4. GPKI 암호화 사용 여부
        // 정부 서버가 요청 바디 복호화 여부를 결정하는 데 사용
        headers.add("gpki_yn", gpkiService.isEnabled() ? "Y" : "N");

        // 5. 트랜잭션 ID
        // 요청 추적, 로그 연관, 문제 해결 시 사용
        headers.add("tx_id", txId);

        // 6. 인증서 서버 ID
        // GPKI 인증서를 발급받은 서버의 식별자
        headers.add("cert_server_id", props.getGpki().getCertServerId());

        // 7. API 인증 키
        // 서비스별로 다른 API 키 사용 가능 (BASIC과 LEDGER 각각)
        headers.add("api_key", svc.getApiKey());

        // 8. CVMIS API 키
        // CVMIS(Car Vehicle Management Information System) 전용 API 키
        headers.add("cvmis_apikey", svc.getCvmisApikey());

        // 구성 완료된 헤더 반환
        return headers;
    }

    /**
     * 자동차 기본사항 조회 API 호출
     *
     * <p>타입 안전성이 보장되는 자동차 기본사항 조회 메서드입니다.
     * 내부적으로 {@link #callModel}을 호출하여 실제 통신을 수행합니다.</p>
     *
     * <h3>특징:</h3>
     * <ul>
     *   <li>제네릭 타입으로 컴파일 타입 타입 체크</li>
     *   <li>요청/응답 객체가 Envelope로 감싸져 있음</li>
     *   <li>Jackson TypeReference를 사용한 제네릭 역직렬화</li>
     *   <li>API 호출 전후로 DB에 로그성 데이터 저장</li>
     * </ul>
     *
     * <h3>처리 흐름:</h3>
     * <ol>
     *   <li>요청 정보를 DB에 INSERT (로그 저장)</li>
     *   <li>정부 API 호출</li>
     *   <li>응답 정보를 DB에 UPDATE</li>
     *   <li>에러 발생 시 에러 정보도 DB에 UPDATE</li>
     * </ol>
     *
     * <h3>사용 예시:</h3>
     * <pre>
     * BasicRequest request = new BasicRequest();
     * request.setVehicleNo("12가3456");
     *
     * Envelope&lt;BasicRequest&gt; envelope = new Envelope&lt;&gt;();
     * envelope.setData(request);
     *
     * ResponseEntity&lt;Envelope&lt;BasicResponse&gt;&gt; response = govClient.callBasic(envelope);
     * BasicResponse data = response.getBody().getData();
     * </pre>
     *
     * @param envelope 자동차 기본사항 조회 요청을 담은 Envelope
     * @return ResponseEntity&lt;Envelope&lt;BasicResponse&gt;&gt; 조회 결과를 담은 응답
     */
    public ResponseEntity<Envelope<BasicResponse>> callBasic(Envelope<BasicRequest> envelope) {
        // 순수한 전송 책임만 수행: DB 로깅은 서비스 레이어에서 처리
        return callModel(ServiceType.BASIC, envelope, new TypeReference<Envelope<BasicResponse>>(){});
    }

    /**
     * 자동차 등록원부(갑) 조회 API 호출
     *
     * <p>타입 안전성이 보장되는 자동차 등록원부 조회 메서드입니다.
     * 내부적으로 {@link #callModel}을 호출하여 실제 통신을 수행합니다.</p>
     *
     * <h3>특징:</h3>
     * <ul>
     *   <li>제네릭 타입으로 컴파일 타임 타입 체크</li>
     *   <li>요청/응답 객체가 Envelope로 감싸져 있음</li>
     *   <li>Jackson TypeReference를 사용한 제네릭 역직렬화</li>
     * </ul>
     *
     * <h3>사용 예시:</h3>
     * <pre>
     * LedgerRequest request = new LedgerRequest();
     * request.setVehicleNo("12가3456");
     * request.setOwnerName("홍길동");
     *
     * Envelope&lt;LedgerRequest&gt; envelope = new Envelope&lt;&gt;();
     * envelope.setData(request);
     *
     * ResponseEntity&lt;Envelope&lt;LedgerResponse&gt;&gt; response = govClient.callLedger(envelope);
     * LedgerResponse data = response.getBody().getData();
     * </pre>
     *
     * @param envelope 자동차 등록원부 조회 요청을 담은 Envelope
     * @return ResponseEntity&lt;Envelope&lt;LedgerResponse&gt;&gt; 조회 결과를 담은 응답
     */
    public ResponseEntity<Envelope<LedgerResponse>> callLedger(Envelope<LedgerRequest> envelope) {
        // TypeReference를 사용하여 제네릭 타입 정보 전달
        // 익명 클래스를 생성하여 타입 소거(Type Erasure) 문제 해결
        return callModel(ServiceType.LEDGER, envelope, new TypeReference<Envelope<LedgerResponse>>(){});
    }

    /**
     * 정부 API 호출 (타입 안전 모델 기반)
     *
     * <p>이 메서드는 정부 API 호출의 핵심 로직을 담고 있는 제네릭 메서드입니다.
     * Java 객체를 받아 JSON으로 변환하고, 암호화하여 전송한 후, 응답을 복호화하여
     * 다시 Java 객체로 변환하는 전체 파이프라인을 처리합니다.</p>
     *
     * <h3>Template Method 패턴:</h3>
     * <ul>
     *   <li>이 메서드는 Template Method 패턴의 템플릿 역할</li>
     *   <li>공통 처리 흐름을 정의하고 서비스별 차이는 파라미터로 처리</li>
     *   <li>코드 중복을 제거하고 일관성을 보장</li>
     * </ul>
     *
     * <h3>제네릭 타입 파라미터:</h3>
     * <ul>
     *   <li>&lt;TReq&gt;: 요청 데이터 타입 (BasicRequest 또는 LedgerRequest)</li>
     *   <li>&lt;TResp&gt;: 응답 데이터 타입 (BasicResponse 또는 LedgerResponse)</li>
     *   <li>타입 안전성을 보장하여 런타임 에러 방지</li>
     * </ul>
     *
     * <h3>처리 흐름 (상세):</h3>
     * <ol>
     *   <li><b>설정 로드:</b>
     *       <ul><li>서비스 타입에 따라 BASIC 또는 LEDGER 설정 선택</li></ul>
     *   </li>
     *   <li><b>URL 및 트랜잭션 ID 구성:</b>
     *       <ul><li>완전한 API URL 생성</li>
     *           <li>고유 트랜잭션 ID 생성</li></ul>
     *   </li>
     *   <li><b>직렬화 (Serialization):</b>
     *       <ul><li>Java 객체(Envelope&lt;TReq&gt;)를 JSON 문자열로 변환</li>
     *           <li>ObjectMapper.writeValueAsString() 사용</li></ul>
     *   </li>
     *   <li><b>헤더 구성:</b>
     *       <ul><li>buildHeaders() 메서드 호출</li>
     *           <li>모든 필수 헤더 추가</li></ul>
     *   </li>
     *   <li><b>GPKI 암호화 (선택적):</b>
     *       <ul><li>GPKI가 활성화된 경우 JSON을 암호화</li>
     *           <li>gpkiEncrypt() 메서드 호출</li></ul>
     *   </li>
     *   <li><b>HTTP 요청 전송:</b>
     *       <ul><li>RestTemplate.exchange()로 POST 요청</li>
     *           <li>요청 로그 기록</li></ul>
     *   </li>
     *   <li><b>GPKI 복호화 (선택적):</b>
     *       <ul><li>성공 응답(2xx)이고 GPKI가 활성화된 경우</li>
     *           <li>gpkiDecrypt() 메서드 호출</li></ul>
     *   </li>
     *   <li><b>역직렬화 (Deserialization):</b>
     *       <ul><li>JSON 문자열을 Java 객체(Envelope&lt;TResp&gt;)로 변환</li>
     *           <li>TypeReference를 사용하여 제네릭 타입 정보 보존</li></ul>
     *   </li>
     *   <li><b>응답 반환:</b>
     *       <ul><li>ResponseEntity로 감싸서 HTTP 정보 포함</li></ul>
     *   </li>
     * </ol>
     *
     * <h3>에러 처리 전략 (3단계):</h3>
     * <ol>
     *   <li><b>HttpStatusCodeException (HTTP 에러):</b>
     *       <ul>
     *         <li>정부 API가 4xx 또는 5xx 상태 코드를 반환한 경우</li>
     *         <li>에러 응답 바디를 파싱하여 Envelope 객체로 변환 시도</li>
     *         <li>파싱 실패 시 빈 Envelope 객체 반환</li>
     *         <li>에러 로그 기록 (WARN 레벨)</li>
     *       </ul>
     *   </li>
     *   <li><b>JSON 파싱 에러:</b>
     *       <ul>
     *         <li>응답 JSON이 예상한 형식과 다른 경우</li>
     *         <li>RuntimeException으로 래핑하여 상위로 전파</li>
     *       </ul>
     *   </li>
     *   <li><b>기타 예외:</b>
     *       <ul>
     *         <li>네트워크 타임아웃, 연결 실패 등</li>
     *         <li>RuntimeException으로 래핑하여 상위로 전파</li>
     *       </ul>
     *   </li>
     * </ol>
     *
     * <h3>TypeReference의 필요성:</h3>
     * <p>Java의 제네릭은 런타임에 타입 소거(Type Erasure)가 발생하여
     * {@code objectMapper.readValue(json, Envelope<TResp>.class)}와 같은 코드는
     * 컴파일되지 않습니다. TypeReference는 익명 클래스를 사용하여 컴파일 타임의
     * 제네릭 타입 정보를 런타임에 전달하는 Jackson의 메커니즘입니다.</p>
     *
     * <h3>로깅 정보:</h3>
     * <ul>
     *   <li>요청 로그: [GOV-REQ] url, tx_id, gpki, length</li>
     *   <li>에러 로그: [GOV-ERR] status, body</li>
     * </ul>
     *
     * @param <TReq> 요청 데이터의 제네릭 타입
     * @param <TResp> 응답 데이터의 제네릭 타입
     * @param type 서비스 타입 (BASIC 또는 LEDGER)
     * @param envelope 요청 데이터를 담은 Envelope 객체
     * @param respType 응답 타입에 대한 TypeReference (제네릭 타입 정보 보존용)
     * @return ResponseEntity&lt;Envelope&lt;TResp&gt;&gt; 응답 데이터를 담은 ResponseEntity
     * @throws RuntimeException JSON 직렬화/역직렬화 실패, 네트워크 오류, GPKI 암복호화 실패 등
     */
    private <TReq, TResp> ResponseEntity<Envelope<TResp>> callModel(ServiceType type,
                                                                    Envelope<TReq> envelope,
                                                                    TypeReference<Envelope<TResp>> respType) {
        // 1. 서비스 타입에 따른 설정 로드
        VmisProperties.GovProps gov = props.getGov();
        VmisProperties.GovProps.Service svc = (type == ServiceType.BASIC)
                ? gov.getServices().getBasic()
                : gov.getServices().getLedger();

        // 2. URL 및 트랜잭션 ID 생성
        String url = gov.buildServiceUrl(svc.getPath());
        String txId = TxIdUtil.generate();

        try {
            // 3. 직렬화: Java 객체 → JSON 문자열
            // ObjectMapper가 Envelope 객체를 JSON으로 변환
            // 날짜, null 값 등의 처리는 ObjectMapper 설정에 따름
            String jsonBody = objectMapper.writeValueAsString(envelope);

            // 4. HTTP 헤더 구성
            HttpHeaders headers = buildHeaders(svc, txId);

            // 5. GPKI 암호화 처리
            String bodyToSend = jsonBody;
            if (gpkiService.isEnabled()) {
                // JSON 평문을 암호화된 문자열로 변환
                bodyToSend = gpkiEncrypt(jsonBody);
            }

            // 6. HTTP 엔티티 생성 (헤더 + 바디)
            HttpEntity<String> request = new HttpEntity<>(bodyToSend, headers);

            // 7. 요청 로그 기록
            log.info("[GOV-REQ] url={}, tx_id={}, gpki={}, length={}", url, txId, gpkiService.isEnabled(), bodyToSend != null ? bodyToSend.length() : 0);

            // 8. 실제 HTTP POST 요청 전송
            ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, request, String.class);
            String respBody = response.getBody();

            // 9. GPKI 복호화 처리 (성공 응답인 경우만)
            if (gpkiService.isEnabled() && response.getStatusCode().is2xxSuccessful()) {
                // 암호화된 응답을 평문 JSON으로 복호화
                respBody = gpkiDecrypt(respBody);
            }

            // 10. 역직렬화: JSON 문자열 → Java 객체
            // TypeReference를 사용하여 제네릭 타입 정보 전달
            Envelope<TResp> mapped = objectMapper.readValue(respBody, respType);

            // 11. 응답 반환 (상태 코드, 헤더, 바디 모두 포함)
            return ResponseEntity.status(response.getStatusCode()).headers(response.getHeaders()).body(mapped);

        } catch (HttpStatusCodeException ex) {
            // HTTP 에러 처리 (4xx, 5xx)
            log.warn("[GOV-ERR] status={}, body={}", ex.getStatusCode(), ex.getResponseBodyAsString());

            // 에러 응답 바디 파싱 시도
            // 정부 API는 에러 응답도 Envelope 형식으로 반환할 수 있음
            Envelope<TResp> empty = new Envelope<>();
            try {
                // 에러 응답을 Envelope 객체로 파싱
                Envelope<TResp> parsed = objectMapper.readValue(ex.getResponseBodyAsString(), respType);
                return ResponseEntity.status(ex.getStatusCode()).headers(ex.getResponseHeaders() != null ? ex.getResponseHeaders() : new HttpHeaders()).body(parsed);
            } catch (Exception parseEx) {
                // 파싱 실패 시 빈 Envelope 반환
                // 호출자는 HTTP 상태 코드로 에러 판단 가능
                return ResponseEntity.status(ex.getStatusCode()).headers(ex.getResponseHeaders() != null ? ex.getResponseHeaders() : new HttpHeaders()).body(empty);
            }
        } catch (Exception e) {
            // 기타 모든 예외 (네트워크 오류, JSON 파싱 오류, GPKI 오류 등)
            // RuntimeException으로 래핑하여 상위로 전파
            // Spring의 @ExceptionHandler에서 처리 가능
            throw new RuntimeException("정부 API 호출 중 오류", e);
        }
    }

    /**
     * GPKI 암호화 처리
     *
     * <p>평문 JSON 문자열을 GPKI(행정전자서명) 알고리즘으로 암호화하는 헬퍼 메서드입니다.
     * 암호화 실패 시 명확한 에러 메시지와 함께 RuntimeException을 발생시킵니다.</p>
     *
     * <h3>암호화 과정:</h3>
     * <ol>
     *   <li>평문 JSON 문자열을 바이트 배열로 변환</li>
     *   <li>정부 시스템의 공개키를 사용하여 암호화</li>
     *   <li>암호화된 바이트 배열을 Base64로 인코딩</li>
     *   <li>Base64 문자열 반환</li>
     * </ol>
     *
     * <h3>에러 처리:</h3>
     * <ul>
     *   <li>인증서 오류: GPKI 인증서가 유효하지 않거나 만료된 경우</li>
     *   <li>암호화 오류: 암호화 알고리즘 실행 중 오류 발생</li>
     *   <li>인코딩 오류: Base64 인코딩 실패</li>
     *   <li>모든 예외는 RuntimeException으로 래핑하여 즉시 중단</li>
     * </ul>
     *
     * <h3>보안 고려사항:</h3>
     * <ul>
     *   <li>공개키 암호화 방식 사용 (비대칭키)</li>
     *   <li>정부 시스템만 개인키로 복호화 가능</li>
     *   <li>민감한 개인정보 보호</li>
     * </ul>
     *
     * @param jsonBody 암호화할 평문 JSON 문자열
     * @return String Base64로 인코딩된 암호화 문자열
     * @throws RuntimeException GPKI 암호화 실패 시
     */
    private String gpkiEncrypt(String jsonBody) {
        try {
            // GpkiService에 암호화 위임
            // 실제 암호화 로직은 GpkiService가 캡슐화
            return gpkiService.encrypt(jsonBody);
        } catch (Exception e) {
            // 암호화 실패는 치명적 오류
            // 평문 데이터를 전송할 수 없으므로 즉시 중단
            throw new RuntimeException("GPKI 암호화 실패", e);
        }
    }

    /**
     * GPKI 복호화 처리
     *
     * <p>암호화된 응답 문자열을 GPKI(행정전자서명) 알고리즘으로 복호화하는 헬퍼 메서드입니다.
     * 복호화 실패 시 명확한 에러 메시지와 함께 RuntimeException을 발생시킵니다.</p>
     *
     * <h3>복호화 과정:</h3>
     * <ol>
     *   <li>Base64로 인코딩된 암호문을 바이트 배열로 디코딩</li>
     *   <li>우리 시스템의 개인키를 사용하여 복호화</li>
     *   <li>복호화된 바이트 배열을 UTF-8 문자열로 변환</li>
     *   <li>평문 JSON 문자열 반환</li>
     * </ol>
     *
     * <h3>에러 처리:</h3>
     * <ul>
     *   <li>인증서 오류: GPKI 인증서가 유효하지 않거나 만료된 경우</li>
     *   <li>복호화 오류: 암호문이 손상되었거나 올바르지 않은 경우</li>
     *   <li>디코딩 오류: Base64 디코딩 실패</li>
     *   <li>문자 인코딩 오류: UTF-8 변환 실패</li>
     *   <li>모든 예외는 RuntimeException으로 래핑하여 즉시 중단</li>
     * </ul>
     *
     * <h3>보안 고려사항:</h3>
     * <ul>
     *   <li>개인키 암호화 방식 사용 (비대칭키)</li>
     *   <li>개인키는 안전하게 저장 및 관리 필요</li>
     *   <li>복호화 실패 시 상세 에러 정보 로깅 주의 (정보 유출 방지)</li>
     * </ul>
     *
     * @param cipher Base64로 인코딩된 암호화 문자열
     * @return String 복호화된 평문 JSON 문자열
     * @throws RuntimeException GPKI 복호화 실패 시
     */
    private String gpkiDecrypt(String cipher) {
        try {
            // GpkiService에 복호화 위임
            // 실제 복호화 로직은 GpkiService가 캡슐화
            return gpkiService.decrypt(cipher);
        } catch (Exception e) {
            // 복호화 실패는 치명적 오류
            // 암호문을 해석할 수 없으므로 즉시 중단
            throw new RuntimeException("GPKI 복호화 실패", e);
        }
    }

}
