package com.pingcap.community.blogserver.service;

import com.pingcap.community.blogserver.entity.RepostRecord;
import com.pingcap.community.blogserver.entity.Tag;
import com.pingcap.community.blogserver.enums.PostOrigin;
import com.pingcap.community.blogserver.enums.RepostPlatform;
import com.pingcap.community.blogserver.exception.repost.FailedToRepostException;
import com.pingcap.community.blogserver.repository.PostAdvancedRepository;
import com.pingcap.community.blogserver.repository.RepostRecordRepository;
import com.pingcap.community.blogserver.support.third.infoq.InfoQClient;
import com.pingcap.community.blogserver.support.third.infoq.dto.InfoQPublishPostDTO;
import com.pingcap.community.blogserver.support.third.infoq.vo.*;
import com.pingcap.community.blogserver.util.PostContentProcessor;
import com.pingcap.community.blogserver.vo.repost.PostNeedRepost;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;

@Slf4j(topic = "repost-service")
@AllArgsConstructor
@Service
public class RepostService {

    private final InfoQClient infoQClient;

    private final PostAdvancedRepository postAdvancedRepository;

    private final RepostRecordRepository repostRecordRepository;

    private final PostContentProcessor postContentProcessor;

    public void repostPosts() {
        log.info("Reposting posts to InfoQ...");
        repostPostsToInfoQ();
    }

    public List<PostNeedRepost> getPostNeedToRepostToPlatform(RepostPlatform platform) {
        Page<PostNeedRepost> postsPage = postAdvancedRepository.getPostsNeedToRepost(platform, PageRequest.of(0, Integer.MAX_VALUE));
        return postsPage.getContent();
    }

    public void repostPostsToInfoQ() {
        List<PostNeedRepost> posts = getPostNeedToRepostToPlatform(RepostPlatform.INFOQ);

        // Get InfoQ API access token.
        InfoQResponse<AuthedVO> accessTokenRes = infoQClient.getAccessToken();
        Optional<String> tokenOptional = Optional.ofNullable(accessTokenRes).map(InfoQResponse::getData).map(AuthedVO::getToken);
        if (tokenOptional.isEmpty()) {
            log.error("Failed to get InfoQ token, skipping to repost posts.");
            return;
        }
        String accessToken = tokenOptional.get();

        // Repost posts.
        int reposts = 0;
        for (PostNeedRepost post : posts) {
            try {
                if (publishPostToInfoQ(post, accessToken)) {
                    reposts++;
                }
                log.info("Repost post {} to platform {} successfully.", post.getPostID(), RepostPlatform.INFOQ.name());
            } catch (FailedToRepostException e) {
                log.error("Failed to repost post {} to platform {}, {}.", post.getPostID(), RepostPlatform.INFOQ.name(), e.getExtra());
            } catch (Exception e) {
                log.error("Failed to repost post {} to platform {}.", post.getPostID(), RepostPlatform.INFOQ.name());
                e.printStackTrace();
            }
        }

        log.info("Successfully repost {} posts to InfoQ.", reposts);
    }

    @Transactional
    public boolean publishPostToInfoQ(PostNeedRepost post, String token) {
        boolean reposted = repostRecordRepository.existsByPostIDAndPlatform(post.getPostID(), RepostPlatform.INFOQ);
        if (reposted) {
            return false;
        }

        // Call API to repost posts.
        InfoQPublishPostDTO publishPostDTO = buildInfoQPublishPostDTO(post, token);
        InfoQResponse<PublishSuccessVO> infoQResponse = infoQClient.publishPost(publishPostDTO);

        InfoQResponseError error = infoQResponse.getError();
        InfoQResponseExtra extra = infoQResponse.getExtra();
        if (error.getCode() != null && !error.getCode().equals(0)) {
            String requestID = Optional.ofNullable(extra).map(InfoQResponseExtra::getRequestId).orElse("");
            String extraInfo = String.format("errorCode: %s errorMsg: %s requestID: %s", error.getCode(), error.getMsg(), requestID);
            throw new FailedToRepostException(post.getPostID(), RepostPlatform.INFOQ, extraInfo);
        }
        PublishSuccessVO resultVO = infoQResponse.getData();
        String postID = resultVO.getId().toString();

        // Add repost record to database.
        RepostRecord repostRecord = new RepostRecord();
        repostRecord.setPostID(post.getPostID());
        repostRecord.setPlatform(RepostPlatform.INFOQ);
        repostRecord.setRepostedPostURL("");
        repostRecord.setRepostedAt(ZonedDateTime.now());
        repostRecord.setRepostedPostKey(postID);
        repostRecordRepository.save(repostRecord);

        return true;
    }

    private InfoQPublishPostDTO buildInfoQPublishPostDTO(PostNeedRepost post, String token) {
        InfoQPublishPostDTO publishPostDTO = new InfoQPublishPostDTO();
        publishPostDTO.setOutID(post.getPostID().toString());
        publishPostDTO.setTitle(post.getTitle());
        publishPostDTO.setSummary(post.getSummary());
        publishPostDTO.setContentMarkdown( post.getMarkdown());
        publishPostDTO.setToken(token);

        // Post origin.
        PostOrigin origin = post.getOrigin();
        if (PostOrigin.ORIGINAL.equals(origin)) {
            publishPostDTO.setCopyright(1);
        } else {
            publishPostDTO.setCopyright(0);
        }

        // Post tags.
        Set<Tag> tags = post.getTags();
        List<String> tagTexts = new ArrayList<>();
        if (tags != null && !tags.isEmpty()) {
            tagTexts = tags.stream().map(Tag::getName).toList();
        }
        publishPostDTO.setTags(tagTexts);

        return publishPostDTO;
    }

}
