package com.pandoli365.bibimbap.service;

import com.pandoli365.bibimbap.data.UnityFeedItemData;
import com.pandoli365.bibimbap.data.UnityFeedSourceData;
import com.pandoli365.bibimbap.mapper.UnityFeedItemsMapper;
import com.pandoli365.bibimbap.mapper.UnityFeedSourcesMapper;
import com.pandoli365.bibimbap.security.SsrfSafeFetcher;
import com.pandoli365.bibimbap.security.SsrfSafeFetcher.FetchResult;
import com.pandoli365.bibimbap.service.FeedParser.FeedItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.net.URI;
import java.util.List;
import java.util.Optional;
import java.util.Set;

@Component
public class UnityFeedPoller {

    private static final Logger log = LoggerFactory.getLogger(UnityFeedPoller.class);

    private static final long FEED_MAX_BYTES = 2L * 1024 * 1024;
    private static final Set<String> FEED_CONTENT_TYPES = Set.of(
            "application/rss+xml",
            "application/atom+xml",
            "application/xml",
            "text/xml",
            "text/html");

    private final SsrfSafeFetcher ssrfSafeFetcher;
    private final FeedParser feedParser;
    private final UnityFeedSourcesMapper sourcesMapper;
    private final UnityFeedItemsMapper itemsMapper;

    public UnityFeedPoller(SsrfSafeFetcher ssrfSafeFetcher,
                           FeedParser feedParser,
                           UnityFeedSourcesMapper sourcesMapper,
                           UnityFeedItemsMapper itemsMapper) {
        this.ssrfSafeFetcher = ssrfSafeFetcher;
        this.feedParser = feedParser;
        this.sourcesMapper = sourcesMapper;
        this.itemsMapper = itemsMapper;
    }

    @Scheduled(fixedDelayString = "${unity.feed.poll-delay-ms:1800000}")
    public void poll() {
        for (UnityFeedSourceData source : sourcesMapper.listActive()) {
            try {
                pollOnce(source.getId());
            } catch (Exception e) {
                // 한 소스 실패가 전체 폴링을 멈추지 않게 격리
                log.warn("unity feed poll 실패 sourceId={}", source.getId(), e);
            }
        }
    }

    public int pollOnce(long sourceId) {
        UnityFeedSourceData source = sourcesMapper.getById(sourceId);
        if (source == null) {
            return 0;
        }
        try {
            URI uri;
            try {
                uri = URI.create(source.getFeedUrl());
            } catch (Exception e) {
                sourcesMapper.updateError(sourceId, "feed URL 파싱 실패");
                return 0;
            }

            Optional<FetchResult> fetched = ssrfSafeFetcher.fetch(uri, FEED_MAX_BYTES, FEED_CONTENT_TYPES);
            if (fetched.isEmpty()) {
                sourcesMapper.updateError(sourceId, "fetch 실패 또는 SSRF 차단");
                return 0;
            }

            List<FeedItem> items = feedParser.parse(fetched.get().body());
            if (items.isEmpty()) {
                sourcesMapper.updateCursor(sourceId, source.getLastSeenGuid());
                return 0;
            }

            int newCount = 0;
            for (FeedItem item : items) {
                if (itemsMapper.existsByGuid(sourceId, item.guid())) {
                    continue;
                }
                UnityFeedItemData data = new UnityFeedItemData();
                data.setSourceId(sourceId);
                data.setGuid(item.guid());
                data.setTitle(item.title());
                data.setLinkUrl(item.link());
                data.setPublishedAt(item.publishedAt());
                if (itemsMapper.insertIgnoreDup(data) == 1) {
                    newCount++;
                }
            }

            sourcesMapper.updateCursor(sourceId, items.get(0).guid());
            return newCount;
        } catch (Exception e) {
            log.warn("unity feed pollOnce 실패 sourceId={}", sourceId, e);
            try {
                sourcesMapper.updateError(sourceId, "폴링 처리 중 오류");
            } catch (Exception ignore) {
                // updateError 자체 실패도 graceful — 전파 금지
            }
            return 0;
        }
    }
}
