package com.cloudhandson.ddsbackoffice.service;

import com.cloudhandson.ddsbackoffice.config.DdsProperties;
import com.cloudhandson.ddsbackoffice.domain.DdsGrantInventorySnapshot;
import com.cloudhandson.ddsbackoffice.domain.DdsGrantPlan;
import com.cloudhandson.ddsbackoffice.domain.DdsPublishedProtectionSpec;
import com.cloudhandson.ddsbackoffice.domain.DdsProtectionOverview;
import com.cloudhandson.ddsbackoffice.domain.DdsProtectionPathStatus;
import com.cloudhandson.ddsbackoffice.domain.DdsValidationEvidence;
import java.time.Instant;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;

/**
 * Produces safe, evidence-based protection status for the DDS console.
 *
 * <p>The status deliberately distinguishes a catalog observation from a full predicate comparison:
 * the deployed catalog query confirms grant/object/grantee, but not the complete predicate or
 * column expression. The UI must therefore never call this observation "fully protected".</p>
 */
@Service
public class DdsProtectionStatusService {

  private final DdsProperties properties;
  private final DdsGrantInventory inventory;
  private final DdsProvisionPlanProvider planProvider;
  private final DdsProtectionEvidenceStore evidenceStore;

  public DdsProtectionStatusService(
      DdsProperties properties,
      DdsGrantInventory inventory,
      DdsProvisionPlanProvider planProvider,
      DdsProtectionEvidenceStore evidenceStore
  ) {
    this.properties = properties;
    this.inventory = inventory;
    this.planProvider = planProvider;
    this.evidenceStore = evidenceStore;
  }

  public DdsProtectionOverview overview() {
    DdsGrantInventorySnapshot snapshot = inventory.observe(properties.vectorObject());
    return new DdsProtectionOverview(servicePath(snapshot), List.of());
  }

  /**
   * Direct comparison can calculate a user-by-user publishing plan. It is intentionally loaded
   * only after an operator opens advanced verification, not while loading the normal dashboard.
   */
  public List<DdsProtectionPathStatus> directComparison() {
    DdsGrantInventorySnapshot snapshot = inventory.observe(properties.vectorObject());
    return directPaths(snapshot);
  }

  private DdsProtectionPathStatus servicePath(DdsGrantInventorySnapshot snapshot) {
    var token = properties.token();
    if (!snapshot.available()) {
      return unavailable("토큰 기반 서비스 경로", "기술 사용자 · " + token.username(), false, snapshot);
    }
    if (!token.configured()) {
      return status(
          "service", "토큰 기반 서비스 경로", "기술 사용자 설정 필요", false,
          "조치 필요", "danger", "기술 사용자 자격증명이 없어 이 경로를 검증할 수 없습니다.",
          "일부 확인", partialObservationDetail(snapshot),
          "판정 불가", "기술 사용자 설정 뒤 객체별 DATA GRANT를 확인합니다.",
          "검증 불가", "Bearer 토큰 검색을 실행할 수 없습니다.", snapshot,
          "지식 검색 설정 확인", "/vector-knowledge"
      );
    }

    if (!snapshot.hasGrantee(token.dataRole())) {
      return status(
          "service", "토큰 기반 서비스 경로", "기술 사용자 · " + token.username(), false,
          "조치 필요", "danger", "이 보호 객체에 토큰용 DATA ROLE Grant가 관측되지 않았습니다.",
          "일부 확인", partialObservationDetail(snapshot),
          "반영 필요", "기대 DATA ROLE " + token.dataRole() + "에 연결된 Grant가 없습니다.",
          "검증 대기", "Grant가 반영된 뒤 Bearer 토큰으로 검색을 확인하세요.", snapshot,
          "지식 검색으로 검증", "/vector-knowledge"
      );
    }

    Verification verification = verification("SERVICE", "service", servicePublished());
    return status(
        "service", "토큰 기반 서비스 경로", "기술 사용자 · " + token.username(), false,
        verification.primaryLabel(), verification.primaryTone(), verification.primaryDescription(),
        "일부 확인", partialObservationDetail(snapshot),
        serviceSynchronizationLabel(), serviceSynchronizationDetail(),
        verification.label(), verification.detail(), snapshot,
        verification.actionLabel(), verification.actionHref()
    );
  }

  private List<DdsProtectionPathStatus> directPaths(DdsGrantInventorySnapshot snapshot) {
    if (!snapshot.available()) {
      return List.of(unavailable("DDS END USER 직접 비교", "직접 비교 상태를 읽지 못했습니다.", true, snapshot));
    }

    try {
      List<DdsGrantPlan> plans = planProvider.preview().grants().stream()
          .filter(plan -> properties.vectorObject().equalsIgnoreCase(plan.ddsObject()))
          .toList();
      if (plans.isEmpty()) {
        return List.of(status(
            "direct", "DDS END USER 직접 비교", "비교 대상 없음", true,
            "확인 필요", "warning", "현재 권한 규칙에서 직접 비교용 벡터 Grant 계획을 만들지 못했습니다.",
            "일부 확인", partialObservationDetail(snapshot),
            "판정 불가", "권한 매핑과 보호 객체 등록을 확인하세요.",
            "검증 대기", "비교 대상을 만든 뒤 직접 조회를 실행할 수 있습니다.", snapshot,
            "권한 반영 보기", "/dds-provision"
        ));
      }
      return plans.stream().map(plan -> directPath(plan, snapshot)).toList();
    } catch (DataAccessException | IllegalArgumentException exception) {
      return List.of(status(
          "direct", "DDS END USER 직접 비교", "직접 비교 계획 확인 필요", true,
          "확인 불가", "danger", "직접 비교용 권한 계획을 계산하지 못했습니다.",
          "일부 확인", partialObservationDetail(snapshot),
          "판정 불가", "권한 원천 또는 보호 객체 매핑을 확인하세요.",
          "검증 불가", "계획을 읽은 뒤 직접 조회를 실행할 수 있습니다.", snapshot,
          "권한 반영 보기", "/dds-provision"
      ));
    }
  }

  private DdsProtectionPathStatus directPath(
      DdsGrantPlan plan,
      DdsGrantInventorySnapshot snapshot
  ) {
    String subject = plan.userLabel() + " · " + plan.dataRole();
    boolean actualGrant = snapshot.hasGrant(plan.grantName());
    if (plan.publishable() && !actualGrant) {
      return status(
          "direct-" + plan.userKey(), "DDS END USER 직접 비교", subject, true,
          "조치 필요", "danger", "현재 권한으로 생성할 Grant가 아직 관측되지 않았습니다.",
          "일부 확인", partialObservationDetail(snapshot),
          "반영 필요", "현재 권한 계획과 catalog Grant가 일치하지 않습니다.",
          "검증 대기", "Grant 반영 뒤 허용·거부 fixture로 확인하세요.", snapshot,
          "권한 반영 미리보기", "/dds-provision"
      );
    }
    Optional<DdsPublishedProtectionSpec> published = published("DIRECT", plan.userKey());
    boolean sourceChanged = published.isPresent()
        && !published.get().fingerprint().equalsIgnoreCase(DdsProtectionFingerprint.direct(plan));
    if (sourceChanged) {
      return status(
          "direct-" + plan.userKey(), "DDS END USER 직접 비교", subject, true,
          "조치 필요", "danger", "권한 규칙이 마지막 DDS 게시 뒤 변경되었습니다.",
          "일부 확인", partialObservationDetail(snapshot),
          "반영 필요", "현재 권한 계획이 마지막 게시 기준과 다릅니다.",
          "검증 만료", "새 권한을 게시한 뒤 fixture 검증을 다시 실행하세요.", snapshot,
          "권한 반영 미리보기", "/dds-provision"
      );
    }
    if (!plan.publishable() && actualGrant) {
      return status(
          "direct-" + plan.userKey(), "DDS END USER 직접 비교", subject, true,
          "조치 필요", "danger", "현재 권한 계획은 기본 거부지만 기존 Grant가 남아 있습니다.",
          "일부 확인", partialObservationDetail(snapshot),
          "차이 발견", "권한을 회수해야 하는 Grant가 catalog에서 관측됩니다.",
          "검증 대기", "반영 후 default deny 결과를 확인하세요.", snapshot,
          "권한 반영 미리보기", "/dds-provision"
      );
    }
    if (!plan.publishable()) {
      return status(
          "direct-" + plan.userKey(), "DDS END USER 직접 비교", subject, true,
          "재검증 필요", "warning", "현재 권한 계획은 이 주체의 기본 거부를 의도합니다.",
          "일부 확인", partialObservationDetail(snapshot),
          "기본 거부 일치", "생성 대상 Grant가 없고 기존 Grant도 관측되지 않았습니다.",
          "검증 기록 없음", "직접 비교에서 객체 미노출 또는 안전한 차단 결과를 확인하세요.", snapshot,
          "직접 비교 실행", "/vector-knowledge"
      );
    }
    Verification verification = verification("DIRECT", plan.userKey(), published);
    return status(
        "direct-" + plan.userKey(), "DDS END USER 직접 비교", subject, true,
        verification.primaryLabel(), verification.primaryTone(), verification.primaryDescription(),
        "일부 확인", partialObservationDetail(snapshot),
        published.isEmpty() ? "게시 기준 미등록" : "선언 관측",
        published.isEmpty() ? "다음 DDS 권한 반영 시 immutable 게시 기준을 기록합니다."
            : "Grant 이름·보호 객체·DATA ROLE이 catalog에서 확인되었습니다.",
        verification.label(), verification.detail(), snapshot,
        verification.actionLabel(), verification.actionHref()
    );
  }

  private DdsProtectionPathStatus unavailable(
      String pathLabel,
      String subject,
      boolean advanced,
      DdsGrantInventorySnapshot snapshot
  ) {
    return status(
        "unavailable-" + pathLabel.toLowerCase(Locale.ROOT), pathLabel, subject, advanced,
        "확인 불가", "danger", "보호 상태를 판정할 inventory를 읽지 못했습니다.",
        "확인 불가", snapshot.safeIssue(),
        "판정 불가", "Grant와 권한 계획의 일치를 비교할 수 없습니다.",
        "검증 불가", "inventory가 복구된 뒤 보호 결과를 확인하세요.", snapshot,
        "보호 연결 다시 확인", "/dds-protection"
    );
  }

  private DdsProtectionPathStatus status(
      String pathKey,
      String pathLabel,
      String subject,
      boolean advanced,
      String primaryLabel,
      String primaryTone,
      String primaryDescription,
      String observationLabel,
      String observationDetail,
      String synchronizationLabel,
      String synchronizationDetail,
      String verificationLabel,
      String verificationDetail,
      DdsGrantInventorySnapshot snapshot,
      String actionLabel,
      String actionHref
  ) {
    return new DdsProtectionPathStatus(
        pathKey, pathLabel, subject, properties.vectorObject(), advanced,
        primaryLabel, primaryTone, primaryDescription,
        observationLabel, observationDetail,
        synchronizationLabel, synchronizationDetail,
        verificationLabel, verificationDetail,
        snapshot.observedAt(), actionLabel, actionHref
    );
  }

  private String partialObservationDetail(DdsGrantInventorySnapshot snapshot) {
    return snapshot.grants().size() + "개 Grant의 이름·보호 객체·DATA ROLE만 확인했습니다. "
        + "predicate와 컬럼 범위는 이 화면에서 비교하지 않습니다.";
  }

  private Optional<DdsPublishedProtectionSpec> servicePublished() {
    return published("SERVICE", "service");
  }

  private Optional<DdsPublishedProtectionSpec> published(String path, String subject) {
    try {
      return evidenceStore.latestPublished(path, subject);
    } catch (DataAccessException exception) {
      return Optional.empty();
    }
  }

  private String serviceSynchronizationLabel() {
    Optional<DdsPublishedProtectionSpec> published = servicePublished();
    if (published.isEmpty()) {
      return "게시 기준 미등록";
    }
    return published.get().fingerprint().equalsIgnoreCase(DdsProtectionFingerprint.service(properties))
        ? "선언 기준 일치" : "기준 갱신 필요";
  }

  private String serviceSynchronizationDetail() {
    return "Grant 이름·보호 객체·DATA ROLE이 catalog에서 확인되었습니다."
        + (servicePublished().isEmpty() ? " 다음 기준 등록 시 immutable fingerprint를 남깁니다." : "");
  }

  private Verification verification(
      String path,
      String subject,
      Optional<DdsPublishedProtectionSpec> published
  ) {
    Optional<DdsValidationEvidence> evidence;
    try {
      evidence = evidenceStore.latestValidation(path, subject);
    } catch (DataAccessException exception) {
      evidence = Optional.empty();
    }
    if (evidence.isEmpty()) {
      return new Verification(
          "검증 기록 없음", "결정적 허용·거부 fixture를 아직 실행하지 않았습니다.",
          "재검증 필요", "warning", "Grant 존재는 확인했지만 최근 권한 결과 검증 기록이 없습니다.",
          "보호 검증 실행", "SERVICE".equals(path) ? "/dds-protection#service-verify" : "/dds-protection/direct"
      );
    }
    DdsValidationEvidence latest = evidence.get();
    if (!latest.passed()) {
      return new Verification(
          "검증 실패", latest.failedCount() + "개 fixture가 기대 결과와 달랐습니다.",
          "조치 필요", "danger", "최근 결정적 권한 검증이 실패했습니다.",
          "권한 반영 미리보기", "/dds-provision"
      );
    }
    if (published.isPresent() && latest.validatedAt().isBefore(published.get().publishedAt())) {
      return new Verification(
          "검증 만료", "마지막 권한 게시 뒤 fixture 검증이 필요합니다.",
          "재검증 필요", "warning", "최근 게시 이후 검증 기록이 없습니다.",
          "보호 검증 실행", "SERVICE".equals(path) ? "/dds-protection#service-verify" : "/dds-protection/direct"
      );
    }
    return new Verification(
        "검증 통과", latest.fixtureCount() + "개 결정적 fixture가 기대 결과와 일치했습니다.",
        "검증 통과", "success", "최근 fixture 검증이 권한 결과와 일치했습니다.",
        "보호 검증 실행", "SERVICE".equals(path) ? "/dds-protection#service-verify" : "/dds-protection/direct"
    );
  }

  private record Verification(
      String label,
      String detail,
      String primaryLabel,
      String primaryTone,
      String primaryDescription,
      String actionLabel,
      String actionHref
  ) {
  }
}
