package com.cloudhandson.vpdbackoffice.service;

import com.cloudhandson.vpdbackoffice.domain.securityscript.SecuritySqlScript;
import com.cloudhandson.vpdbackoffice.domain.securityscript.SecuritySqlScriptSummary;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

/**
 * Read-only catalogue of security deployment SQL bundled from the Git-tracked
 * sql/adb directory. Script ids are an application whitelist: request input
 * never becomes a filesystem or classpath path.
 */
@Service
public class SecuritySqlScriptService {

  private static final List<ScriptDefinition> CURATED_SCRIPTS = List.of(
      new ScriptDefinition(
          "aso-masking-metadata",
          "ASO / 마스킹",
          "62_kb_aso_masking_backoffice_metadata.sql",
          "컬럼 마스킹 규칙 메타데이터",
          "ASO 컬럼 마스킹 규칙·컬럼 연결·사용자 예외를 관리하는 백오피스 메타데이터를 생성합니다."
      ),
      new ScriptDefinition(
          "aso-masking-runtime",
          "ASO / 마스킹",
          "63_kb_aso_masking_rule_runtime.sql",
          "ASO 마스킹 런타임 적용",
          "백오피스 컬럼 마스킹 규칙을 Oracle Data Redaction 정책과 신뢰 컨텍스트에 반영합니다."
      ),
      new ScriptDefinition(
          "aso-masking-default-columns",
          "ASO / 마스킹",
          "64_kb_aso_masking_default_column_rules.sql",
          "ASO 기본 대상 컬럼",
          "주민번호·청구/지급금·타사보유 컬럼의 마스킹 블랙리스트 초기값을 연결합니다."
      ),
      new ScriptDefinition(
          "select-ai-vpd-api",
          "Select AI / 행 접근",
          "65_kb_select_ai_vpd_query_api.sql",
          "행 접근 적용 Select AI 조회 API",
          "생성 SQL을 KB 업무 테이블의 단일 읽기 전용 SELECT/WITH로 검증해 행 접근 컨텍스트에서 실행합니다."
      ),
      new ScriptDefinition(
          "select-ai-vpd-ords",
          "ORDS / Select AI",
          "66_kb_select_ai_vpd_query_ords.sql",
          "Select AI 행 접근 ORDS Endpoint",
          "Bearer 토큰을 검증해 행 접근 컨텍스트를 설정한 뒤 Select AI 조회 API를 노출합니다."
      )
  );

  public List<SecuritySqlScriptSummary> list() {
    return CURATED_SCRIPTS.stream()
        .map(definition -> new SecuritySqlScriptSummary(
            definition.scriptId(),
            definition.category(),
            definition.fileName(),
            definition.title(),
            definition.description()
        ))
        .toList();
  }

  public SecuritySqlScript find(String scriptId) {
    ScriptDefinition definition = CURATED_SCRIPTS.stream()
        .filter(candidate -> candidate.scriptId().equals(scriptId))
        .findFirst()
        .orElseThrow(() -> new AppException("조회할 수 없는 보안 SQL 스크립트입니다."));
    return new SecuritySqlScript(
        definition.scriptId(),
        definition.category(),
        definition.fileName(),
        definition.title(),
        definition.description(),
        readSource(definition.fileName())
    );
  }

  private String readSource(String fileName) {
    ClassPathResource resource = new ClassPathResource("sql/adb/" + fileName);
    try (InputStream input = resource.getInputStream()) {
      return new String(input.readAllBytes(), StandardCharsets.UTF_8);
    } catch (IOException exception) {
      throw new AppException("배포된 보안 SQL 스크립트를 읽을 수 없습니다: " + fileName);
    }
  }

  private record ScriptDefinition(
      String scriptId,
      String category,
      String fileName,
      String title,
      String description
  ) {
  }
}
