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

import java.util.Optional;
import java.util.UUID;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import ru.ibs.socrates.confexporter.dao.dto.ContentDaoDto;
import ru.ibs.socrates.confexporter.dao.entity.MessageEntity;
import ru.ibs.socrates.confexporter.external.chanker.model.DocumentRequest;
import ru.ibs.socrates.confexporter.external.pageparser.model.ParserRequest;
import ru.ibs.socrates.confexporter.external.sdtllmraggateconf.model.ContentHttpViewModel;
import ru.ibs.socrates.confexporter.external.xmlparser.model.XmlParserDocumentRequest;
import ru.ibs.socrates.confexporter.service.content.service.ProcessContentService.ContentHolder;

/**
 * Маппер для преобразования между сущностями и DTO контента.
 * Обеспечивает:
 * <ul>
 *   <li>Преобразование {@link MessageEntity} ↔ {@link ContentResponse}</li>
 *   <li>Создание {@link ContentCreate} из внешних DTO</li>
 * </ul>
 */
@Mapper
public interface ContentMapper {

  /**
   * Создает DTO для создания контента из запроса от rabbitmq.
   *
   * @param hldr
   * @return
   */
  @Mapping(target = "externalMessagelId", source = "hldr.content.id")
  @Mapping(target = "projectSourceId", source = "hldr.content.sourceId")
  @Mapping(target = "projectTitle", source = "hldr.source.name")
  @Mapping(target = "projectComment", source = "hldr.source.comment")
  @Mapping(target = "contentType", source = "hldr.content",
      qualifiedByName = "getType")
  @Mapping(target = "text", source = "hldr.parsedContent")
  @Mapping(target = "link", source = "hldr.text.link")
  ContentDaoDto toContentDaoDto(ContentHolder hldr);

  /**
   * Маппинг content и text на запрос в чанкер.
   * @param hldr
   * @return
   */
  @Mapping(target = "title", source = "hldr.content.title")
  @Mapping(target = "type", source = "hldr.content", qualifiedByName = "getType")
  @Mapping(target = "link", source = "hldr.text.link")
  @Mapping(target = "idExternal", source = "hldr.content.id")
  @Mapping(target = "projectId", source = "hldr.content.sourceId")
  @Mapping(target = "content", source = "hldr.parsedContent")
  DocumentRequest toDocumentRequest(ContentHolder hldr);

  /**
   * Маппинг на запрос для pageparser.
   * @param rawContent
   * @return
   */
  ParserRequest toParserRequest(
      String rawContent);

  /**
   * Маппинг на запрос для xmlparser.
   *
   * @param hldr
   * @return
   */
  @Mapping(target = "title", source = "hldr.content.title")
  @Mapping(target = "idExternal", source = "hldr.content.id")
  @Mapping(target = "projectId", source = "hldr.content.sourceId")
  @Mapping(target = "content", source = "hldr.parsedContent")
  XmlParserDocumentRequest toXmlDocumentRequest(ContentHolder hldr);

  /**
   * Получение типа страницы.
   * @param content
   * @return
   */
  @Named("getType")
  default String getType(ContentHttpViewModel content) {
    var type = Optional.ofNullable(content)
        .map(ContentHttpViewModel::getType)
        .map(String::toLowerCase)
        .orElse("");
    var fileExtensionXml = Optional.ofNullable(content)
        .map(ContentHttpViewModel::getDownloadUrl)
        .map(String::toLowerCase)
        .filter(link -> link.endsWith(".xml"))
        .isPresent();
    return switch (type) {
      case "file" -> {
        if (fileExtensionXml) {
          yield "XML";
        } else {
          yield "FILE";
        }
      }
      case "page" -> "PAGE";
      case "xml" -> "XML";
      case "folder" -> "FOLDER";
      default -> "";
    };
  }

  /**
   * Получение медиа типа страницы.
   *
   * @param content
   * @return
   */
  @Named("getMediaType")
  default String getMediaType(ContentHttpViewModel content) {
    return Optional.ofNullable(content)
        .map(ContentHttpViewModel::getMediaType)
        .map(String::toUpperCase)
        .orElse("");
  }

  /**
   * Преобразует UUID в строку.
   *
   * @param uuid идентификатор (может быть null)
   * @return строковое представление UUID или null, если входной объект null
   */
  default String uuidToString(UUID uuid) {
    return Optional.ofNullable(uuid)
        .map(UUID::toString)
        .orElse(null);
  }
}
