package ru.micord.ervu.service.grid.impl;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.HashSet;
import java.util.Optional;
import java.util.stream.Stream;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import ru.micord.ervu.exception.JsonParsingException;
import ru.micord.ervu.journal.JournalDto;
import ru.micord.ervu.journal.JournalFileDataRequest;
import ru.micord.ervu.journal.JournalFileDataResponse;
import ru.micord.ervu.journal.mapper.JournalDtoMapper;
import ru.micord.ervu.security.exception.UnauthorizedException;
import ru.micord.ervu.service.InteractionService;
import ru.micord.ervu.service.grid.InMemoryStaticGridLoadService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import ru.micord.ervu.kafka.service.ReplyingKafkaService;
import ru.micord.ervu.security.webbpm.jwt.service.JwtTokenService;

@Service
public class JournalInMemoryStaticGridLoadService implements
    InMemoryStaticGridLoadService<JournalDto> {

  private final JwtTokenService jwtTokenService;
  private final InteractionService interactionService;
  private final ReplyingKafkaService replyingKafkaService;
  private final ObjectMapper objectMapper;
  private final HttpServletRequest request;

  @Value("${ervu.kafka.journal.request.topic}")
  private String requestTopic;
  @Value("${ervu.kafka.journal.reply.topic}")
  private String replyTopic;
  @Value("${db.journal.excluded.statuses:}")
  private String[] excludedStatuses;

  public JournalInMemoryStaticGridLoadService(JwtTokenService jwtTokenService,
                                              InteractionService interactionService,
                                              ReplyingKafkaService replyingKafkaService,
                                              ObjectMapper objectMapper, HttpServletRequest request) {
    this.jwtTokenService = jwtTokenService;
    this.interactionService = interactionService;
    this.replyingKafkaService = replyingKafkaService;
    this.objectMapper = objectMapper;
    this.request = request;
  }

  @Override
  public List<JournalDto> loadData() {
    JournalFileDataRequest journalFileDataRequest = initJournalFileDataRequest();
    List<JournalDto> dbJournalList = interactionService.get(journalFileDataRequest.getOrgIdErvu(),
        excludedStatuses)
        .stream().map(JournalDtoMapper::mapToJournalDto)
        .toList();
    List<JournalDto> ervuJournalList;

    try {
      String responseJsonString = replyingKafkaService.sendMessageAndGetReply(requestTopic,
          replyTopic, objectMapper.writeValueAsString(journalFileDataRequest));
      JournalFileDataResponse journalFileDataResponse = objectMapper.readValue(responseJsonString,
          JournalFileDataResponse.class);
      ervuJournalList = journalFileDataResponse.getFilesInfo().stream()
          .map(JournalDtoMapper::mapToJournalDto)
          .toList();
    }
    catch (JsonProcessingException e) {
      throw new JsonParsingException("Failed to parse JournalFileDataResponse.", e);
    }

    HashSet<String> seenFileIds = new HashSet<>();
    return Stream.concat(dbJournalList.stream(), ervuJournalList.stream())
        .filter(journal -> seenFileIds.add(journal.getFileId()))
        .sorted(Comparator.comparing(JournalDto::getDepartureDateTime))
        .toList();
  }

  private JournalFileDataRequest initJournalFileDataRequest() {
    String authToken = Optional.ofNullable(request.getCookies())
        .map(cookies -> Arrays.stream(cookies)
            .filter(cookie -> cookie.getName().equals("auth_token"))
            .findFirst()
            .map(Cookie::getValue)
            .orElseThrow(() -> new UnauthorizedException("Failed to get auth data. User unauthorized.")))
        .orElseThrow(() -> new UnauthorizedException("Failed to get auth data. User unauthorized."));
    String[] ids = jwtTokenService.getToken(authToken).getUserAccountId().split(":");
    String userId = ids[0];
    String ervuId = ids[1];
    return new JournalFileDataRequest()
        .setPrnOid(userId)
        .setOrgIdErvu(ervuId);
  }
}
