package ru.ibs.socrates.confexporter.service.content.service.impl;

import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import ru.ibs.socrates.confexporter.dao.service.MessageDaoService;
import ru.ibs.socrates.confexporter.external.chanker.model.DocumentRequest;
import ru.ibs.socrates.confexporter.external.chanker.model.DocumentResponse;
import ru.ibs.socrates.confexporter.external.pageparser.client.PageparserApiClient;
import ru.ibs.socrates.confexporter.external.sdtllmraggateconf.api.ContentApi;
import ru.ibs.socrates.confexporter.external.sdtllmraggateconf.api.SourceApi;
import ru.ibs.socrates.confexporter.external.sdtllmraggateconf.api.TextApi;
import ru.ibs.socrates.confexporter.external.sdtllmraggateconf.model.ContentHttpViewModel;
import ru.ibs.socrates.confexporter.external.sdtllmraggateconf.model.ReadTextHttpResponse;
import ru.ibs.socrates.confexporter.external.sdtllmraggateconf.model.SourceHttpViewModel;
import ru.ibs.socrates.confexporter.external.sdtllmraggateconf.rabbitmq.dto.SdtLlmRagGateEventDto;
import ru.ibs.socrates.confexporter.external.xmlparser.client.XmlparserApiClient;
import ru.ibs.socrates.confexporter.service.content.enums.EventTypeEnum;
import ru.ibs.socrates.confexporter.service.content.mapper.ContentMapper;
import ru.ibs.socrates.confexporter.service.content.service.ProcessContentService;

/**
 * Сервис обработки контента, получаемого из внешних систем.
 * Обеспечивает:
 * <ul>
 *   <li>Обработку событий создания/обновления/удаления контента</li>
 *   <li>Взаимодействие с внешним API для получения данных</li>
 *   <li>Сохранение контента в БД через {@link MessageDaoService}</li>
 * </ul>
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ProcessContentServiceImpl implements ProcessContentService {

  private final ru.ibs.socrates.confexporter.external.chanker.api.DefaultApi chankerApi;
  private final SourceApi sourceApi;
  private final ContentApi contentApi;
  private final TextApi textApi; // Клиент OpenAPI для работы с текстовым API
  private final XmlparserApiClient xmlparserApiClient;
  private final PageparserApiClient pageparserApiClient;
  private final ContentMapper contentMapper; // Маппер для преобразования DTO
  private final MessageDaoService contentService; // Сервис работы с контентом

  @Override
  public void processContent(SdtLlmRagGateEventDto event) {
    try {
      processContent(event.getMessage().getId(), event.getMessage().getEventName());
    } catch (Exception e) {
      log.error("{}", e.getMessage(), e);
    }
  }

  /**
   * Обрабатывает контент на основе внешнего события.
   *
   * @param externalMessagelIdStr строковый идентификатор сообщения (должен быть валидным UUID)
   * @param eventTypeStr тип события (поддерживаются: CREATE, UPDATE, DELETE)
   * @throws IllegalArgumentException если:
   *         <ul>
   *           <li>externalMessagelIdStr не является валидным UUID</li>
   *           <li>eventTypeStr содержит неизвестный тип события</li>
   *         </ul>
   * @throws jakarta.persistence.EntityNotFoundException если контент для удаления не найден
   * @throws org.springframework.web.client.RestClientException при ошибках взаимодействия с TextApi
   */
  @Override
  public void processContent(String externalMessagelIdStr, String eventTypeStr) {
    UUID externalMessagelId = UUID.fromString(externalMessagelIdStr);
    var event = EventTypeEnum.of(eventTypeStr);
    switch (event) {
      case CREATE -> create(externalMessagelId);
      case UPDATE -> create(externalMessagelId);
      case DELETE -> contentService.deleteContent(externalMessagelId);
      default -> throw new IllegalArgumentException("Unexpected value: " + event);
    }
  }

  /**
   * Создает или обновляет контент.
   * <ol>
   *   <li>Запрашивает данные через {@link TextApi}</li>
   *   <li>Преобразует ответ в DTO</li>
   *   <li>Сохраняет через {@link MessageDaoService}</li>
   * </ol>
   *
   * @param externalMessagelId идентификатор контента (не null)
   * @throws org.springframework.web.client.RestClientException при ошибках вызова API
   * @throws jakarta.persistence.EntityNotFoundException если не удалось сохранить контент
   */
  private void create(UUID externalMessagelId) {
    // Вызываем REST API Content
    ContentHttpViewModel contentResponse =
        contentApi.apiV1ContentIdReadGet(externalMessagelId.toString()).getBody();
    // Вызываем REST API Text
    ReadTextHttpResponse textResponse =
        textApi.apiV1TextIdReadGet(externalMessagelId.toString()).getBody();
    // Вызываем REST API Source
    SourceHttpViewModel sourceResponse = sourceApi
        .apiV1SourceSourceIdReadGet(contentResponse.getSourceId()).getBody();
    var contentHolder = new ContentHolder(sourceResponse, contentResponse, textResponse, null);
    String parsedContent = getParsedContent(contentHolder);
    contentHolder = new ContentHolder(sourceResponse, contentResponse, textResponse, parsedContent);
    if (checkContentTypeIsNotValid(contentResponse)) {
      return;
    }
    if (checkMediaTypeIsDrawio(contentResponse)) {
      saveToXmlparser(contentHolder);
      return;
    }
    saveToChanker(contentHolder);
    saveToDb(contentHolder);
  }
  
  /**
   * Получаем parsed верстию контента.
   * @param holder
   * @return
   */
  private String getParsedContent(ContentHolder holder) {
    String textContent = holder.text().getText();
    String messageType = contentMapper.getType(holder.content());
    if (List.of("PAGE").contains(messageType)) {
      return pageparserApiClient.parsedTextParsedTextPost(
          contentMapper.toParserRequest(textContent))
          .getBody()
          .getParsedContent();
    }
    return textContent;
  }
  
  /**
   * Проверяем, что тип сообщения можно обработать.
   * @param contentResponse
   * @return
   */
  private boolean checkContentTypeIsNotValid(ContentHttpViewModel contentResponse) {
    String messageType = contentMapper.getType(contentResponse);
    // Сохраняем в БД
    if (!List.of("PAGE", "FILE", "XML").contains(messageType)) {
      log.warn("unknown type {} {}", contentResponse.getId(), messageType);
      return true;
    }
    return false;
  }

  /**
   * Проверяет, что медиа тип сообщения является Drawio.
   * @param contentResponse
   * @return
   */
  private boolean checkMediaTypeIsDrawio(ContentHttpViewModel contentResponse) {
    String mediaType = contentMapper.getMediaType(contentResponse);
    return mediaType.equals("DRAWIO");
  }

  /**
   * Сохраняем в Чанкер.
   * @param holder
   * @return
   */
  private void saveToChanker(ContentHolder holder) {
    DocumentRequest documentRequest = contentMapper
        .toDocumentRequest(holder);
    // Сохраняем в Чанкер
    DocumentResponse documentResponse =
        chankerApi.parseDocumentProcessTextPost(documentRequest).getBody();
    log.info("{} отправлен в chanker и получен статус {}", documentResponse.getSuccess());
  }

  /**
   * Сохраняем в xml parser.
   * @param holder
   */
  private void saveToXmlparser(ContentHolder holder) {
    var documentRequest = contentMapper.toXmlDocumentRequest(holder);
    var documentResponse = xmlparserApiClient.processSchemaProcessSchemaPost(documentRequest);
    log.info("{} отправлен в xml parser и получен статус {}",
        documentRequest.getIdExternal(), documentResponse.getStatusCode());
  }
  
  private void saveToDb(ContentHolder holder) {
    var contentCreate = contentMapper.toContentDaoDto(holder);
    contentService.createContent(contentCreate);
  }
  
}
