package com.cloudhandson.ddsbackoffice.service;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import com.cloudhandson.ddsbackoffice.config.DdsProperties;
import com.cloudhandson.ddsbackoffice.domain.DdsGrantInventoryEntry;
import com.cloudhandson.ddsbackoffice.domain.DdsGrantInventorySnapshot;
import com.cloudhandson.ddsbackoffice.domain.DdsGrantPlan;
import com.cloudhandson.ddsbackoffice.domain.DdsPublishedProtectionSpec;
import com.cloudhandson.ddsbackoffice.domain.DdsProvisioningPlan;
import com.cloudhandson.ddsbackoffice.domain.DdsValidationEvidence;
import com.cloudhandson.ddsbackoffice.domain.DdsValidationFixture;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.junit.jupiter.api.Test;

class DdsProtectionStatusServiceTest {

  @Test
  void neverMarksAnUnavailableInventoryAsProtected() {
    DdsGrantInventory inventory = object -> new DdsGrantInventorySnapshot(
        false, List.of(), Instant.parse("2026-06-30T00:00:00Z"), "catalog 접근 실패"
    );
    var service = new DdsProtectionStatusService(
        properties(), inventory, emptyPlanProvider(), emptyEvidenceStore());

    var overview = service.overview();

    assertEquals("확인 불가", overview.servicePath().primaryLabel());
    assertEquals("확인 불가", overview.servicePath().observationLabel());
    assertTrue(overview.servicePath().needsAction());
  }

  @Test
  void separatesMissingTokenGrantFromDirectComparisonEvidence() {
    DdsGrantInventory inventory = object -> new DdsGrantInventorySnapshot(
        true,
        List.of(new DdsGrantInventoryEntry(
            "DDS_DEMO_BOTH_VECTOR_GRANT", "CB_DDS_VECTOR_SEARCH_DOCUMENTS", "DDS_DEMO_BOTH_ROLE")),
        Instant.parse("2026-06-30T00:00:00Z"), ""
    );
    DdsProvisionPlanProvider provider = () -> new DdsProvisioningPlan(List.of(new DdsGrantPlan(
        "both", "통합 사용자", 103L, "dds_demo_both_role", "CB_VECTOR_SEARCH_DOCUMENTS",
        "ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS", "DDS_DEMO_BOTH_VECTOR_GRANT", "1 = 1", "",
        "CREATE DATA GRANT", true, "게시 가능"
    )), List.of(), 1);
    var service = new DdsProtectionStatusService(properties(), inventory, provider, emptyEvidenceStore());

    var overview = service.overview();

    assertEquals("조치 필요", overview.servicePath().primaryLabel());
    assertEquals("반영 필요", overview.servicePath().synchronizationLabel());
    var direct = service.directComparison();
    assertEquals(1, direct.size());
    assertEquals("재검증 필요", direct.getFirst().primaryLabel());
    assertEquals("게시 기준 미등록", direct.getFirst().synchronizationLabel());
  }

  @Test
  void reportsDirectGrantDriftWhenCurrentPlanHasNoGrantButInventoryDoes() {
    DdsGrantInventory inventory = object -> new DdsGrantInventorySnapshot(
        true,
        List.of(new DdsGrantInventoryEntry(
            "DDS_DEMO_BOTH_VECTOR_GRANT", "CB_DDS_VECTOR_SEARCH_DOCUMENTS", "DDS_DEMO_BOTH_ROLE")),
        Instant.parse("2026-06-30T00:00:00Z"), ""
    );
    DdsProvisionPlanProvider provider = () -> new DdsProvisioningPlan(List.of(new DdsGrantPlan(
        "both", "통합 사용자", 103L, "dds_demo_both_role", "CB_VECTOR_SEARCH_DOCUMENTS",
        "ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS", "DDS_DEMO_BOTH_VECTOR_GRANT", "", "", "",
        false, "ALLOW 규칙 없음"
    )), List.of(), 1);
    var service = new DdsProtectionStatusService(properties(), inventory, provider, emptyEvidenceStore());

    var direct = service.directComparison().getFirst();

    assertEquals("조치 필요", direct.primaryLabel());
    assertEquals("차이 발견", direct.synchronizationLabel());
  }

  private static DdsProperties properties() {
    return new DdsProperties(
        "jdbc:test", Duration.ofSeconds(5), "ADMIN.V_DDS_CUSTOMERS_PG", "ADMIN.V_DDS_CUSTOMERS_MY",
        "ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS",
        Map.of("both", new DdsProperties.User(
            "통합 사용자", "직접 비교", "dds_demo_both", "secret", 103L, "dds_demo_both_role")),
        Map.of("CB_VECTOR_SEARCH_DOCUMENTS", "ADMIN.CB_DDS_VECTOR_SEARCH_DOCUMENTS"),
        new DdsProperties.Token("dds_demo_token", "secret", "cb_dds_token_role")
    );
  }

  private static DdsProvisionPlanProvider emptyPlanProvider() {
    return () -> new DdsProvisioningPlan(List.of(), List.of(), 0);
  }

  private static DdsProtectionEvidenceStore emptyEvidenceStore() {
    return new DdsProtectionEvidenceStore() {
      @Override
      public Optional<DdsPublishedProtectionSpec> latestPublished(String path, String subjectKey) {
        return Optional.empty();
      }

      @Override
      public Optional<DdsValidationEvidence> latestValidation(String path, String subjectKey) {
        return Optional.empty();
      }

      @Override
      public List<DdsValidationFixture> activeFixtures(String path) {
        return List.of();
      }

      @Override
      public void recordPublished(DdsPublishedProtectionSpec spec, String actor, String note) {
      }

      @Override
      public void recordValidation(String runId, String fixtureId, String path, String subjectKey,
          boolean expectedVisible, boolean actualVisible, String fingerprint) {
      }
    };
  }
}
