package com.cloudhandson.vpdbackoffice.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.cloudhandson.vpdbackoffice.domain.schemametadata.SchemaMetadataAnnotationRow;
import com.cloudhandson.vpdbackoffice.domain.schemametadata.SchemaMetadataColumnRow;
import com.cloudhandson.vpdbackoffice.domain.schemametadata.SchemaMetadataView;
import com.cloudhandson.vpdbackoffice.domain.structured.StructuredDataTable;
import com.cloudhandson.vpdbackoffice.mapper.SchemaMetadataMapper;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class SchemaMetadataServiceTest {

  private static final StructuredDataTable GAME_USERS = new StructuredDataTable(
      "game-users", "CZN_COMN_USER_MST", "게임 사용자", "카제나 게임 사용자 마스터");

  private SchemaMetadataMapper mapper;
  private SchemaMetadataService service;

  @BeforeEach
  void setUp() {
    mapper = mock(SchemaMetadataMapper.class);
    StructuredDataService structuredDataService = mock(StructuredDataService.class);
    DataCatalog catalog = mock(DataCatalog.class);
    when(catalog.owner()).thenReturn("SGMP_POC");
    when(structuredDataService.requireTable("game-users")).thenReturn(GAME_USERS);
    when(structuredDataService.tables()).thenReturn(List.of(GAME_USERS));
    when(structuredDataService.defaultKey()).thenReturn("game-users");
    service = new SchemaMetadataService(mapper, structuredDataService, catalog);
  }

  @Test
  void readsOnlyTheSelectedSmilegateTableThroughMapper() {
    when(mapper.findTableComment("SGMP_POC", "CZN_COMN_USER_MST"))
        .thenReturn("게임별 사용자 기준 정보");
    when(mapper.findAnnotations("CZN_COMN_USER_MST")).thenReturn(List.of(
        new SchemaMetadataAnnotationRow(null, "BUSINESS_TERM", "게임 사용자"),
        new SchemaMetadataAnnotationRow("GUID", "BUSINESS_TERM", "사용자 고유 식별자"),
        new SchemaMetadataAnnotationRow("GUID", "BUSINESS_TERM", "고객 계정 식별자")
    ));
    when(mapper.findColumns("SGMP_POC", "CZN_COMN_USER_MST")).thenReturn(List.of(
        new SchemaMetadataColumnRow("GUID", "VARCHAR2(64)", "N", "사용자 GUID")
    ));

    SchemaMetadataView view = service.find("game-users");

    assertThat(view.tableComment()).isEqualTo("게임별 사용자 기준 정보");
    assertThat(view.tableAnnotations()).singleElement()
        .extracting(annotation -> annotation.name(), annotation -> annotation.value())
        .containsExactly("BUSINESS_TERM", "게임 사용자");
    assertThat(view.columns()).singleElement()
        .satisfies(column -> {
          assertThat(column.columnName()).isEqualTo("GUID");
          assertThat(column.nullable()).isFalse();
          assertThat(column.annotations()).singleElement()
              .extracting(annotation -> annotation.value())
              .isEqualTo("사용자 고유 식별자\n--- duplicate annotation value ---\n고객 계정 식별자");
        });
    verify(mapper).findAnnotations("CZN_COMN_USER_MST");
    verify(mapper).findColumns("SGMP_POC", "CZN_COMN_USER_MST");
  }

  @Test
  void replacesAnExistingColumnAnnotationUsingMapperDdl() {
    when(mapper.countColumn("SGMP_POC", "CZN_COMN_USER_MST", "GUID")).thenReturn(1);
    when(mapper.countColumnAnnotation("CZN_COMN_USER_MST", "GUID", "BUSINESS_TERM"))
        .thenReturn(1);

    service.updateColumnAnnotation("game-users", "guid", "business_term", "사용자 고유 식별자");

    verify(mapper).dropColumnAnnotation("SGMP_POC", "CZN_COMN_USER_MST", "GUID", "BUSINESS_TERM");
    verify(mapper).addColumnAnnotation(
        "SGMP_POC", "CZN_COMN_USER_MST", "GUID", "BUSINESS_TERM", "'사용자 고유 식별자'");
  }

  @Test
  void clearsAnExistingTableAnnotationWithoutAddingAnEmptyValue() {
    when(mapper.countTableAnnotation("CZN_COMN_USER_MST", "BUSINESS_TERM"))
        .thenReturn(1);

    service.updateTableAnnotation("game-users", "business_term", " ");

    verify(mapper).dropTableAnnotation("SGMP_POC", "CZN_COMN_USER_MST", "BUSINESS_TERM");
    verify(mapper, never()).addTableAnnotation(anyString(), anyString(), anyString(), anyString());
  }

  @Test
  void rejectsAColumnThatIsNotPresentInTheSelectedTable() {
    when(mapper.countColumn("SGMP_POC", "CZN_COMN_USER_MST", "UNKNOWN_COL")).thenReturn(0);

    assertThatThrownBy(() -> service.updateColumnComment("game-users", "unknown_col", "설명"))
        .isInstanceOf(AppException.class)
        .hasMessage("선택한 테이블에 존재하지 않는 컬럼입니다.");
  }
}
