<!doctype html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<body>
<div th:fragment="result" class="probe-result-flow">
  <div class="alert alert-danger" th:if="${errorMessage}" th:text="${errorMessage}"></div>
  <th:block th:if="${result}">
  <div class="section-heading">
    <div>
      <span class="architecture-kicker">검증 결론</span>
      <h2 th:text="${result.title()}">권한 결과</h2>
    </div>
    <span class="badge"
          th:classappend="${result.successLike()} ? ' text-bg-success' : ' text-bg-warning'"
          th:text="${result.successLike()} ? '검증 완료' : '확인 필요'">검증 완료</span>
  </div>

  <p class="probe-summary" th:text="${result.plainSummary()}">결과 설명</p>

  <section class="result-section">
    <div class="section-heading compact-heading">
      <h3>검증 흐름 요약</h3>
      <span class="badge text-bg-light">토큰 → VPD → ASO → FGA</span>
    </div>
    <div class="effective-preview">
      <dl>
        <div>
          <dt>1. 토큰 해석</dt>
          <dd th:if="${tokenContext}" th:text="${tokenContext.username() + ' 사용자로 해석됐습니다.'}">사용자 확인</dd>
          <dd th:unless="${tokenContext}">유효한 사용자로 해석되지 않았습니다. 이 경우 DB context가 설정되지 않아 권한이 없습니다.</dd>
        </div>
        <div>
          <dt>2. 행 접근(VPD)</dt>
          <dd th:text="${result.rowCount() == 0 ? 'VPD 적용 후 반환 가능한 행이 없습니다.' : 'VPD 적용 후 반환 가능한 행이 있습니다.'}">행 접근 결과</dd>
        </div>
        <div>
          <dt>3. 컬럼 마스킹(ASO)</dt>
          <dd th:if="${!#lists.isEmpty(result.maskedColumns())}" th:text="${#strings.listJoin(result.maskedColumns(), ', ') + ' 컬럼이 마스킹 또는 NULL로 관찰됐습니다.'}">마스킹 컬럼</dd>
          <dd th:if="${#lists.isEmpty(result.maskedColumns())}">응답에서 마스킹 또는 NULL로 관찰된 민감 컬럼이 없습니다. 원문 허용이거나 해당 컬럼이 결과에 없을 수 있습니다.</dd>
        </div>
        <div>
          <dt>4. DB 감사 증적(FGA)</dt>
          <dd th:text="${result.hasFgaExecutionEvidence() ? '이번 요청 ID와 일치하는 DB 감사 행을 찾았습니다.' : '이번 결과에 연결된 DB 감사 행은 찾지 못했습니다.'}">FGA 결과</dd>
        </div>
      </dl>
    </div>
  </section>

  <section class="result-section">
    <div class="section-heading compact-heading">
      <h3>적용된 사용자와 권한</h3>
      <span class="badge text-bg-light" th:if="${tokenContext}" th:text="${tokenContext.statusLabel()}">ACTIVE</span>
    </div>
    <div class="effective-preview" th:if="${tokenContext}">
      <dl>
        <div><dt>사용자</dt><dd th:text="${tokenContext.username()}">user</dd></div>
        <div>
          <dt>직접 받은 역할</dt>
          <dd th:if="${!#lists.isEmpty(tokenContext.directRoles())}" th:text="${#strings.listJoin(tokenContext.directRoles(), ', ')}">ROLE</dd>
          <dd th:if="${#lists.isEmpty(tokenContext.directRoles())}" class="text-muted">없음</dd>
        </div>
        <div>
          <dt>소속 그룹</dt>
          <dd th:if="${!#lists.isEmpty(tokenContext.groups())}" th:text="${#strings.listJoin(tokenContext.groups(), ', ')}">GROUP</dd>
          <dd th:if="${#lists.isEmpty(tokenContext.groups())}" class="text-muted">없음</dd>
        </div>
        <div>
          <dt>그룹에서 상속된 역할</dt>
          <dd th:if="${!#lists.isEmpty(tokenContext.inheritedRoles())}" th:text="${#strings.listJoin(tokenContext.inheritedRoles(), ', ')}">ROLE</dd>
          <dd th:if="${#lists.isEmpty(tokenContext.inheritedRoles())}" class="text-muted">없음</dd>
        </div>
      </dl>
    </div>
    <div class="alert alert-light mb-0" th:unless="${tokenContext}">
      토큰에서 사용자를 찾지 못해 역할과 그룹을 계산하지 않았습니다.
    </div>
  </section>

  <section class="result-section">
    <div class="section-heading compact-heading">
      <h3>행 접근 정책이 적용된 실제 결과</h3>
      <span th:if="${selectedObject}"><code th:text="${selectedObject.displayName()}">ADMIN.OBJECT</code></span>
    </div>
    <div class="result-metrics">
      <div><span>행 접근 후 남은 행</span><strong th:text="${result.rowCount()}">0</strong></div>
      <div>
        <span>ASO 마스킹 확인 컬럼</span>
        <strong th:if="${!#lists.isEmpty(result.maskedColumns())}" th:text="${#strings.listJoin(result.maskedColumns(), ', ')}">column</strong>
        <strong th:if="${#lists.isEmpty(result.maskedColumns())}">없음</strong>
      </div>
    </div>
    <div class="effective-preview mt-3">
      <dl>
        <div>
          <dt>행 접근 판단</dt>
          <dd th:text="${result.rowCount() == 0 ? '토큰 context와 접근 규칙 기준으로 반환 가능한 행이 없습니다.' : '토큰 context와 접근 규칙 기준으로 반환 가능한 행이 있습니다.'}">반환 가능</dd>
        </div>
        <div>
          <dt>ASO 컬럼 표시 판단</dt>
          <dd th:if="${!#lists.isEmpty(result.maskedColumns())}" th:text="${'마스킹 또는 NULL로 반환된 민감 컬럼: ' + #strings.listJoin(result.maskedColumns(), ', ')}">마스킹 컬럼</dd>
          <dd th:if="${#lists.isEmpty(result.maskedColumns())}">응답 기준으로 전체 NULL/마스킹 처리된 민감 컬럼을 찾지 못했습니다. 원문 허용이거나 해당 컬럼이 응답에 없을 수 있습니다.</dd>
        </div>
        <div>
          <dt>확인 기준</dt>
          <dd>행 개수는 행 접근 정책 결과이고, 컬럼 값은 ASO/Data Redaction 결과입니다. 두 판단은 별도로 봐야 합니다.</dd>
        </div>
      </dl>
    </div>

    <div class="vector-result-panel mt-3"
         th:if="${vectorSearch and !#lists.isEmpty(result.rows())}">
      <div class="section-heading compact-heading">
        <div>
          <h3>벡터 검색 Top-K</h3>
          <p class="section-subtitle">거리(SCORE)가 낮은 순서로 행 접근 정책을 통과한 검색 단위를 반환했습니다.</p>
        </div>
        <span class="badge text-bg-light" th:text="${'K=' + result.rowCount()}">K=0</span>
      </div>
      <p class="form-hint" th:text="${'검색어: ' + vectorQuery + ' · 임베딩: ' + vectorEmbeddingMode + ' · 모델: ' + vectorEmbeddingModel}">검색 정보</p>
      <div class="table-responsive mt-3">
        <table class="table table-sm align-middle">
          <thead>
          <tr>
            <th>검색 단위 ID</th>
            <th>자료 ID</th>
            <th>제목</th>
            <th>기술 태그</th>
            <th>벡터 거리 (SCORE)</th>
            <th>본문</th>
          </tr>
          </thead>
          <tbody>
          <tr th:each="row : ${result.rows()}">
            <td th:text="${row['CHUNK_ID'] ?: row['chunk_id']}">28001</td>
            <td th:text="${row['DOCUMENT_ID'] ?: row['document_id']}">knowledge-001</td>
            <td th:text="${row['TITLE'] ?: row['title']}">제목</td>
            <td><code th:text="${row['TECH_TAG'] ?: row['tech_tag']}">ORDS</code></td>
            <td th:text="${row['SCORE'] ?: row['score']}">0.01</td>
            <td class="matrix-list" th:text="${row['CHUNK_TEXT'] ?: row['chunk_text']}">본문</td>
          </tr>
          </tbody>
        </table>
      </div>
    </div>

    <div class="table-responsive mt-3" th:if="${!vectorSearch and !#lists.isEmpty(result.rows())}">
      <table class="table table-sm table-striped align-middle">
        <thead><tr><th th:each="column : ${result.columns()}" th:text="${column}">column</th></tr></thead>
        <tbody>
        <tr th:each="row : ${result.rows()}">
          <td th:each="column : ${result.columns()}" th:text="${row.get(column)}">value</td>
        </tr>
        </tbody>
      </table>
    </div>
  </section>

  <section class="result-section" th:if="${result.hasFgaExecutionEvidence()}">
    <div class="section-heading compact-heading">
      <div>
        <h3>DB 감사 실행 증적</h3>
        <p class="section-subtitle">이번 요청 ID와 일치하는 DB 감사 행입니다. 화면에서 재계산한 값이 아닙니다.</p>
      </div>
      <span class="badge text-bg-success">FGA</span>
    </div>
    <div class="effective-preview">
      <dl>
        <div><dt>감사 시각</dt><dd th:text="${result.fgaExecutionEvidence().eventAt()} ?: '-'">2026-07-01T10:00:00Z</dd></div>
        <div><dt>DB 실행 사용자</dt><dd th:text="${result.fgaExecutionEvidence().dbUser()} ?: '-'">CB_ORDS</dd></div>
        <div><dt>요청 식별자</dt><dd><code th:text="${result.fgaExecutionEvidence().clientId()} ?: '-'">request-id</code></dd></div>
        <div><dt>문장 종류</dt><dd th:text="${result.fgaExecutionEvidence().statementType()} ?: '-'">SELECT</dd></div>
      </dl>
    </div>
    <div class="probe-exchange-grid mt-3">
      <section class="probe-exchange" data-sql-trace-field="executed_sql">
        <h3>DB가 감사한 실행 SQL</h3>
        <p class="form-hint">FGA의 SQL_TEXT입니다.</p>
        <pre th:text="${result.fgaExecutionEvidence().sqlText()}">SELECT ...</pre>
      </section>
      <section class="probe-exchange" data-sql-trace-field="fga_rls_info" th:if="${result.fgaExecutionEvidence().hasRlsInfo()}">
        <h3>DB가 감사한 VPD predicate</h3>
        <p class="form-hint">FGA의 RLS_INFO입니다. 적용된 VPD 정책명과 predicate가 기록됩니다.</p>
        <pre th:text="${result.fgaExecutionEvidence().rlsInfo()}">RLS_INFO</pre>
      </section>
    </div>
    <div class="alert alert-light mt-3 mb-0" th:if="${result.fgaExecutionEvidenceMessage() != null}"
         th:text="${result.fgaExecutionEvidenceMessage()}"></div>
  </section>

  <section class="result-section sql-trace-section" th:if="${result.hasSqlTrace()}">
    <div class="section-heading compact-heading">
      <div>
        <h3>참고용 SQL 재현</h3>
        <p class="section-subtitle"
           th:text="${vectorSearch ? 'VECTOR_DISTANCE 검색과 행 접근 규칙을 조합한 참고용 표현입니다.' : '현재 행 접근 규칙을 조합한 참고용 표현입니다.'}">
          현재 행 접근 규칙을 조합한 참고용 표현입니다.
        </p>
      </div>
      <span class="badge text-bg-light">참고용</span>
    </div>
    <div class="probe-exchange-grid mt-3">
      <section class="probe-exchange" data-sql-trace-field="vpd_context" th:if="${tokenContext}">
        <h3>set_vpd_context 사용자 컨텍스트</h3>
        <p class="form-hint">이 컨텍스트로 행 접근 규칙을 계산한 뒤 아래 VPD predicate와 effective SQL을 만들었습니다.</p>
        <dl class="mb-0">
          <div><dt>CB_AGENT_CTX.USER_ID</dt><dd th:text="${tokenContext.userId()}">103</dd></div>
          <div><dt>사용자</dt><dd th:text="${tokenContext.username()}">김어드민</dd></div>
          <div>
            <dt>직접 역할</dt>
            <dd th:text="${#lists.isEmpty(tokenContext.directRoles()) ? '없음' : #strings.listJoin(tokenContext.directRoles(), ', ')}">ALL_DOC_ROLE</dd>
          </div>
          <div>
            <dt>소속 그룹</dt>
            <dd th:text="${#lists.isEmpty(tokenContext.groups()) ? '없음' : #strings.listJoin(tokenContext.groups(), ', ')}">없음</dd>
          </div>
          <div>
            <dt>상속 역할</dt>
            <dd th:text="${#lists.isEmpty(tokenContext.inheritedRoles()) ? '없음' : #strings.listJoin(tokenContext.inheritedRoles(), ', ')}">없음</dd>
          </div>
        </dl>
      </section>
      <section class="probe-exchange" data-sql-trace-field="vector_search" th:if="${vectorSearch}">
        <h3>벡터 유사도 검색 기준</h3>
        <pre>VECTOR_DISTANCE(d.embedding, TO_VECTOR(:embedding), COSINE) AS score
ORDER BY score ASC
ROWNUM &lt;= :row_limit</pre>
        <p class="form-hint mb-0">이 부분이 검색어 벡터와 저장 벡터의 거리 계산입니다. 아래에는 실제로 등록된 역할 기반 행 접근 조건만 표시됩니다.</p>
      </section>
      <section class="probe-exchange" data-sql-trace-field="vpd_predicate">
        <h3 th:text="${vectorSearch ? '역할 기반 권한 필터 (재현)' : '행 접근 조건 (VPD predicate 재현)'}">행 접근 조건 (VPD predicate 재현)</h3>
        <pre th:if="${result.vpdPredicate() == '1 = 1'}">1 = 1 (ALL: 추가 행 필터 없음)</pre>
        <pre th:unless="${result.vpdPredicate() == '1 = 1'}" th:text="${result.vpdPredicate()}">(DEPT_CODE = SYS_CONTEXT('CB_AGENT_CTX', 'DEPT_CODE'))</pre>
        <p class="form-hint mb-0" th:if="${vectorSearch}">선택한 사용자의 직접 역할·그룹 상속 역할에 연결된 permission rule에서 계산됩니다. 기본 whitelist 역할의 ALL은 추가 행 필터 없이 조회를 허용하고, 실제로 TAG·부서 조건을 등록한 역할만 그 조건이 SQL에 들어갑니다. 권한이 없으면 <code>1 = 0</code>입니다.</p>
      </section>
      <section class="probe-exchange" data-sql-trace-field="effective_sql">
        <h3>SQL (재현)</h3>
        <pre th:text="${result.effectiveSql()}">SELECT ... WHERE (...) AND ROWNUM &lt;= ...</pre>
      </section>
    </div>
    <p class="form-hint mt-2 mb-0">
      이 영역은 행 접근 규칙을 읽기 쉽게 재현한 표현일 뿐, 실행 증적이 아닙니다. 실제 SQL과 VPD predicate는 위의 DB 감사 실행 증적(FGA)으로 확인합니다. 컬럼 마스킹은 별도 Redaction 정책입니다.
    </p>
  </section>

  <div class="alert alert-light mb-0" th:if="${result.successLike() and !result.hasFgaExecutionEvidence() and result.fgaExecutionEvidenceMessage() != null}">
    <strong>DB 감사 실행 증적</strong>
    <span th:text="${result.fgaExecutionEvidenceMessage()}">감사 행을 찾지 못했습니다.</span>
  </div>

  <div class="alert alert-light mb-0" th:if="${result.successLike() and !result.hasSqlTrace()}">
    이 Handler는 SQL trace 정보를 반환하지 않았고 운영 DB에서도 기본 VPD predicate를 조회하지 못했습니다. <a href="/ords-handlers">조회 연동</a>에서 trace가 포함된 소스를 적용하고, <code>CB_AGENT_DOC_VPD_FILTER</code> 실행 권한을 확인하세요.
  </div>

  <section class="next-action-card">
    <h3>다음에 할 일</h3>
    <p th:text="${result.nextAction()}">다음 행동</p>
    <div class="action-stack">
      <a class="btn btn-sm rw-btn-secondary" href="/effective-matrix">사용자별 최종 권한 보기</a>
      <a class="btn btn-sm rw-btn-secondary" href="/permissions">행 접근 규칙 보기</a>
      <a class="btn btn-sm rw-btn-secondary" href="/tokens" th:if="${result.status().name() == 'TOKEN_NOT_FOUND' || result.status().name() == 'TOKEN_INACTIVE'}">새 토큰 발급</a>
    </div>
  </section>

  <details class="technical-details">
    <summary>기술 상세: 오류 코드와 HTTP 요청·응답 보기</summary>
    <div class="alert alert-warning mt-3" th:if="${result.errorCode()}">
      <strong th:text="${result.errorCode()}">ERROR</strong>
      <span th:text="${result.errorMessage()}">message</span>
    </div>
    <div class="probe-exchange-grid mt-3">
      <section class="probe-exchange"><h3>Request Headers</h3><pre th:text="${result.requestHeaders()} ?: '{}'">{}</pre></section>
      <section class="probe-exchange"><h3>Request Payload</h3><pre th:text="${result.requestPayload()} ?: '{}'">{}</pre></section>
      <section class="probe-exchange"><h3>Response Headers</h3><pre th:text="${result.responseHeaders()} ?: '{}'">{}</pre></section>
      <section class="probe-exchange"><h3>Response Body</h3><pre th:text="${result.responseBody()} ?: ''"></pre></section>
    </div>
  </details>
  </th:block>
</div>
</body>
</html>
