package com.tarpha.torrssen2.service;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import com.rometools.rome.feed.synd.SyndEntry;
import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.io.SyndFeedInput;
import com.rometools.rome.io.XmlReader;
import com.tarpha.torrssen2.domain.DownloadList;
import com.tarpha.torrssen2.domain.RssFeed;
import com.tarpha.torrssen2.domain.RssList;
import com.tarpha.torrssen2.domain.SeenList;
import com.tarpha.torrssen2.domain.Setting;
import com.tarpha.torrssen2.domain.WatchList;
import com.tarpha.torrssen2.repository.DownloadListRepository;
import com.tarpha.torrssen2.repository.DownloadPathRepository;
import com.tarpha.torrssen2.repository.RssFeedRepository;
import com.tarpha.torrssen2.repository.RssListRepository;
import com.tarpha.torrssen2.repository.SeenListRepository;
import com.tarpha.torrssen2.repository.SettingRepository;
import com.tarpha.torrssen2.repository.WatchListRepository;
import com.tarpha.torrssen2.util.CommonUtils;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class RssLoadService {
    @Autowired
    private RssListRepository rssListRepository;

    @Autowired
    private RssFeedRepository rssFeedRepository;

    @Autowired
    private WatchListRepository watchListRepository;

    @Autowired
    private SeenListRepository seenListRepository;

    @Autowired
    private SettingRepository settingRepository;

    @Autowired
    private DownloadListRepository downloadListRepository;

    @Autowired
    private DownloadPathRepository downloadPathRepository;

    @Autowired
    private TransmissionService transmissionService;

    @Autowired
    private HttpDownloadService httpDownloadService;

    @Autowired
    private DownloadStationService downloadStationService;

    @Autowired
    private DaumMovieTvService daumMovieTvService;

    @Autowired
    private RssMakeService rssMakeService;

    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    public void loadRss() {
        log.info("=== Load RSS ===");

        deleteFeed();

        List<RssFeed> rssFeedList = new ArrayList<RssFeed>();

        for(RssFeed rssFeed: rssMakeService.makeRss()) {
            if (!rssFeedRepository.findByLink(rssFeed.getLink()).isPresent()) {
                rssFeedList.add(rssFeed);

                Optional<RssList> optioalRss = rssListRepository.findByName(rssFeed.getRssSite());
                if(optioalRss.isPresent()) {
                    if(optioalRss.get().getDownloadAll()) {
                        download(rssFeed, optioalRss.get());
                    }
                }

                // Watch List를 체크하여 다운로드 요청한다.
                checkWatchList(rssFeed, null);
            }
        }

        for (RssList rss : rssListRepository.findByUseDbAndInternal(true, false)) {
            log.info("Load RSS Site : " + rss.getName());
            try {
                URL feedSource = new URL(rss.getUrl());
                SyndFeedInput input = new SyndFeedInput();
                SyndFeed feedList = input.build(new XmlReader(feedSource));

                for (int i = feedList.getEntries().size() - 1; i >= 0; i--) {
                    //Feed 한 건이 오류가 발생하여도 전체 로드에는 문제 없도록 예외처리를 추가한다.
                    try {
                        SyndEntry feed = feedList.getEntries().get(i);
                        RssFeed rssFeed = new RssFeed();

                        // log.debug(feed.toString());

                        if (!rssFeedRepository.findByLink(rssFeed.getLinkByKey(rss.getLinkKey(), feed)).isPresent()) {
                            rssFeed.setTitle(feed.getTitle());
                            rssFeed.setRssSite(rss.getName());
                            rssFeed.setTvSeries(rss.getTvSeries());

                            rssFeed.setRssTitleByTitle(feed.getTitle());
                            rssFeed.setRssEpisodeByTitle(feed.getTitle());
                            rssFeed.setRssSeasonByTitle(feed.getTitle());
                            rssFeed.setRssQualityBytitle(feed.getTitle());
                            rssFeed.setRssReleaseGroupByTitle(feed.getTitle());
                            rssFeed.setRssDateBytitle(feed.getTitle());
                            rssFeed.setLinkByKey(rss.getLinkKey(), feed);

                            try {
                                if(!StringUtils.isEmpty(feed.getDescription().getValue())) {
                                    rssFeed.setDesc(feed.getDescription().getValue());
                                }
                            } catch (NullPointerException ne) {
                                log.debug("description: " + ne.toString());
                            }

                            String[] rssTitleSplit = StringUtils.split(rssFeed.getRssTitle());
                            if (rssTitleSplit.length == 1) {
                                rssFeed.setRssPoster(daumMovieTvService.getPoster(rssFeed.getRssTitle()));
                            } else {
                                for (int j = rssTitleSplit.length - 1; j > 0; j--) {
                                    StringBuffer posterTitle = new StringBuffer();
                                    for (int k = 0; k <= j; k++) {
                                        posterTitle.append(rssTitleSplit[k] + " ");
                                    }
                                    String posterUrl = daumMovieTvService
                                            .getPoster(StringUtils.trim(posterTitle.toString()));
                                    if (!StringUtils.isEmpty(posterUrl)) {
                                        rssFeed.setRssPoster(posterUrl);
                                        break;
                                    }
                                }
                            }
                            // rssFeed.setRssPoster(daumMovieTvService.getPoster(rssFeed.getRssTitle()));
                            rssFeedList.add(rssFeed);

                            log.debug("Add Feed: " + rssFeed.getTitle());

                            if(rss.getDownloadAll()) {
                                log.debug("RSS Download Repuest All: " + rssFeed.getTitle());
                                download(rssFeed, rss);
                            }

                            // Watch List를 체크하여 다운로드 요청한다.
                            checkWatchList(rssFeed, null);
                        }
                    } catch (Exception e) {
                        //Feed 개별 건에 대한 Exception 처리
                        log.error(e.getMessage());
                    }
                }
            } catch (Exception e) {
                //Feed Site에 대한 Exception 처리
                log.error(e.getMessage());
            }
        }

        rssFeedRepository.saveAll(rssFeedList);
        // rssFeedRepository.saveAll(rssMakeService.makeRss());

        simpMessagingTemplate.convertAndSend("/topic/feed/update", true);
    }

    public void checkWatchListFromDb(List<WatchList> list) {
        for (RssFeed rssFeed : rssFeedRepository.findAll()) {
            checkWatchList(rssFeed, list);
        }
    }

    @Async
    public void asyncLoadRss() {
        loadRss();
    }

    public boolean checkWatchListQuality(RssFeed rssFeed, WatchList watchList) {
        boolean checkQuality = false;

        if (StringUtils.isBlank(watchList.getQuality())) {
            watchList.setQuality("100p");
        }

        try {
            if (StringUtils.contains(watchList.getQuality(), ',')) {
                checkQuality = StringUtils.containsIgnoreCase(watchList.getQuality(), rssFeed.getRssQuality());

            } else if (StringUtils.endsWithIgnoreCase(watchList.getQuality(), "P+")) {
                checkQuality = Integer.parseInt(StringUtils.removeIgnoreCase(rssFeed.getRssQuality(), "P")) 
                    >= Integer.parseInt(StringUtils.removeIgnoreCase(watchList.getQuality(), "P+"));
            } else {
                checkQuality = Integer.parseInt(StringUtils.removeIgnoreCase(rssFeed.getRssQuality(), "P")) 
                    == Integer.parseInt(StringUtils.removeIgnoreCase(watchList.getQuality(), "P"));
            }
        } catch (NumberFormatException e) {
            log.error(e.getMessage());
        }

        return checkQuality;
    }
 
    /**
     * Feed가 WatchList에 존재할 경우 다운로드 요청을 한다.
     * 
     * @param rssFeed   WatchList에 존재하는지 검사할 대상이 되는 개별 RSS Feed 
     * @param list      사용자가 화면에서 WatchList 개별 건을 선택해서 직접 실행 했을 때 그 List를 받아올 인자
     */
    private void checkWatchList(RssFeed rssFeed, List<WatchList> list) {

        if (StringUtils.isBlank(rssFeed.getRssQuality())) {
            rssFeed.setRssQuality("100p");
        }
        Optional<WatchList> optionalWatchList = watchListRepository.findByTitleRegex(rssFeed.getTitle(),
            rssFeed.getRssQuality());

        if (optionalWatchList.isPresent()) {
            WatchList watchList = optionalWatchList.get();
            log.info("Matched Feed: " + rssFeed.getTitle());

            rssFeed.setWatchTitle(watchList.getTitle());

            try {
                if (watchList.getRssList() != null && watchList.getRssList().size() > 0) {
                    if (!watchList.getRssList().contains(rssFeed.getRssSite())) {
                        log.info("Skipped by RSS List");
                        return;
                    }
                }
            } catch (NullPointerException e) {
                log.error(e.toString());
            }

            if(list != null) {
                log.info("Custom Execute");
                boolean isExists = false;
                for(WatchList wl : list) {
                    if(StringUtils.equals(watchList.getTitle(), wl.getTitle())) {
                        log.info("Custom Execute Matched: " + rssFeed.getTitle());
                        isExists = true;
                        break;
                    }
                }

                if(!isExists) {
                    log.info("Custom Execute Not Matched: " + rssFeed.getTitle());
                    return;
                }
            }

            boolean seenDone = false;
            boolean subtitleDone = false;
            // boolean checkQuality = false;

            // if (StringUtils.isBlank(watchList.getQuality())) {
            //     watchList.setQuality("100p");
            // }

            // try {
            //     if (StringUtils.endsWithIgnoreCase(watchList.getQuality(), "P+")) {
            //         checkQuality = Integer.parseInt(StringUtils.removeIgnoreCase(rssFeed.getRssQuality(), "P")) 
            //             >= Integer.parseInt(StringUtils.removeIgnoreCase(watchList.getQuality(), "P+"));
            //     } else {
            //         checkQuality = Integer.parseInt(StringUtils.removeIgnoreCase(rssFeed.getRssQuality(), "P")) 
            //             == Integer.parseInt(StringUtils.removeIgnoreCase(watchList.getQuality(), "P"));
            //     }
            // } catch (NumberFormatException e) {
            //     log.error(e.getMessage());
            // }

            // if (!checkQuality) {
            if(!checkWatchListQuality(rssFeed, watchList)) {
                log.info("Rejected by Quality: " + rssFeed.getTitle());
                return;
            }

            if (watchList.getSeries()) {
                if (StringUtils.contains(watchList.getQuality(), ',')) {
                    // if (seenListRepository.countByParams(rssFeed.getLink(), rssFeed.getRssTitle(), rssFeed.getRssSeason(),
                    if (seenListRepository.countByParams(rssFeed.getLink(), rssFeed.getWatchTitle(), rssFeed.getRssSeason(),
                        rssFeed.getRssEpisode(), false, rssFeed.getRssQuality()) > 0) {
                        seenDone = true;
                    }

                    // if (seenListRepository.countByParams(rssFeed.getLink(), rssFeed.getRssTitle(), rssFeed.getRssSeason(),
                    if (seenListRepository.countByParams(rssFeed.getLink(), rssFeed.getWatchTitle(), rssFeed.getRssSeason(),
                            rssFeed.getRssEpisode(), true, rssFeed.getRssQuality()) > 0 || !watchList.getSubtitle()) {
                        subtitleDone = true;
                    }
                } else {
                    // if (seenListRepository.countByParams(rssFeed.getLink(), rssFeed.getRssTitle(), rssFeed.getRssSeason(),
                    if (seenListRepository.countByParams(rssFeed.getLink(), rssFeed.getWatchTitle(), rssFeed.getRssSeason(),
                        rssFeed.getRssEpisode(), false) > 0) {
                        seenDone = true;
                    }

                    // if (seenListRepository.countByParams(rssFeed.getLink(), rssFeed.getRssTitle(), rssFeed.getRssSeason(),
                    if (seenListRepository.countByParams(rssFeed.getLink(), rssFeed.getWatchTitle(), rssFeed.getRssSeason(),
                            rssFeed.getRssEpisode(), true) > 0 || !watchList.getSubtitle()) {
                        subtitleDone = true;
                    }
                }  
            } else {
                if (seenListRepository.findFirstByLinkAndSubtitle(rssFeed.getLink(), false).isPresent()) {
                    seenDone = true;
                }

                if (seenListRepository.findFirstByLinkAndSubtitle(rssFeed.getLink(), true).isPresent()
                        || !watchList.getSubtitle()) {
                    subtitleDone = true;
                }
            }

            if (seenDone && subtitleDone) {
                log.info("Rejected by Seen: " + rssFeed.getTitle());
            } else {
                if (!watchList.getSubtitle() && StringUtils.contains(rssFeed.getTitle(), "자막")
                        && !StringUtils.startsWith(rssFeed.getLink(), "magnet")) {
                    log.info("Rejected by Subtitle: " + rssFeed.getTitle());
                    return;
                }
                if (watchList.getSeries()) {
                    try {
                        int startSeason = Integer.parseInt(watchList.getStartSeason());
                        int endSeason = Integer.parseInt(watchList.getEndSeason());
                        int startEpisode = Integer.parseInt(watchList.getStartEpisode());
                        int endEpisode = Integer.parseInt(watchList.getEndEpisode());
                        int currSeason = Integer.parseInt(rssFeed.getRssSeason());
                        int currEpisode = Integer.parseInt(rssFeed.getRssEpisode());

                        if (currSeason < startSeason || currSeason > endSeason) {
                            log.info("Rejected by Season: Start: " + startSeason + " End: " + endSeason + " Feed: "
                                    + currSeason);
                            return;
                        }
                        if (currEpisode < startEpisode || currEpisode > endEpisode) {
                            log.info("Rejected by Episode: Start: " + startEpisode + " End: " + endEpisode + " Feed: "
                                    + currEpisode);
                            return;
                        }
                    } catch (NumberFormatException e) {
                        log.error(e.toString());
                    }
                }

                log.info("Download Repuest: " + rssFeed.getTitle());

                download(rssFeed, watchList);
            }
        }
    }

    private void addToSeenList(RssFeed rssFeed, String path, String rename) {
        SeenList seenList = new SeenList();
        // seenList.setTitle(rssFeed.getRssTitle());
        seenList.setTitle(rssFeed.getWatchTitle());
        seenList.setLink(rssFeed.getLink());
        seenList.setDownloadPath(path);
        seenList.setSeason(rssFeed.getRssSeason());
        seenList.setEpisode(rssFeed.getRssEpisode());
        seenList.setRenameStatus(StringUtils.isBlank(rename) ? "N/A" : "false");
        seenList.setQuality(rssFeed.getRssQuality());

        if (StringUtils.contains(rssFeed.getTitle(), "자막") && !StringUtils.startsWith(rssFeed.getLink(), "magnet")) {
            seenList.setSubtitle(true);
            seenList.setTitle("[자막]" + rssFeed.getRssTitle());
        }

        seenListRepository.save(seenList);
    }

    private void addToDownloadList(Long id, RssFeed rssFeed, WatchList watchList, String path) {
        DownloadList download = new DownloadList();
        download.setId(id);
        download.setName(rssFeed.getTitle());
        download.setUri(rssFeed.getLink());
        download.setDownloadPath(path);
        if (!StringUtils.isBlank(watchList.getRename())) {
            download.setRename(CommonUtils.getRename(watchList.getRename(), rssFeed.getRssTitle(),
                    rssFeed.getRssSeason(), rssFeed.getRssEpisode(), rssFeed.getRssQuality(),
                    rssFeed.getRssReleaseGroup(), rssFeed.getRssDate()));
        }

        downloadListRepository.save(download);
    }

    private void addToDownloadList(Long id, RssFeed rssFeed, String path) {
        DownloadList download = new DownloadList();
        download.setId(id);
        download.setName(rssFeed.getTitle());
        download.setUri(rssFeed.getLink());
        download.setDownloadPath(path);

        downloadListRepository.save(download);
    }

    private void addToDownloadList(DownloadList download, RssFeed rssFeed, WatchList watchList) {
        if (!StringUtils.isBlank(watchList.getRename())) {
            download.setRename(CommonUtils.getRename(watchList.getRename(), rssFeed.getRssTitle(),
                    rssFeed.getRssSeason(), rssFeed.getRssEpisode(), rssFeed.getRssQuality(),
                    rssFeed.getRssReleaseGroup(), rssFeed.getRssDate()));
        }

        downloadListRepository.save(download);
    }

    @Transactional
    private void deleteFeed() {
        Optional<Setting> optionalSetting = settingRepository.findByKey("USE_LIMIT");
        if (optionalSetting.isPresent()) {
            if (Boolean.parseBoolean(optionalSetting.get().getValue())) {
                Optional<Setting> limitCnt = settingRepository.findByKey("LIMIT_COUNT");
                if (limitCnt.isPresent()) {
                    int cnt = Integer.parseInt(limitCnt.get().getValue());
                    rssFeedRepository.deleteByLimitCount(cnt);
                    downloadListRepository.deleteByLimitCount(cnt);
                }
            }
        }
    }

    private void download(RssFeed rssFeed, WatchList watchList) {
        String path = downloadPathRepository.computedPath(watchList.getDownloadPath(), rssFeed.getRssTitle(),
            rssFeed.getRssSeason());
        if (StringUtils.isBlank(path)) {
            path = watchList.getDownloadPath();
        }

        Optional<Setting> optionalSetting = settingRepository.findByKey("DOWNLOAD_APP");
        if (optionalSetting.isPresent()) {
            if (StringUtils.equals(optionalSetting.get().getValue(), "TRANSMISSION")) {
                // Request Download to Transmission
                if (StringUtils.startsWith(rssFeed.getLink(), "magnet")
                    || StringUtils.equalsIgnoreCase(FilenameUtils.getExtension(rssFeed.getLink()), "torrent")) {
                    int torrentAddedId = transmissionService.torrentAdd(rssFeed.getLink(), path);
                    log.debug("Transmission ID: " + torrentAddedId);
    
                    if (torrentAddedId > 0) {
                        // Add to Seen
                        addToSeenList(rssFeed, path, watchList.getRename());
    
                        // Add to Download List
                        addToDownloadList((long) torrentAddedId, rssFeed, watchList, path);
                    }
                } else {
                    Optional<DownloadList> optionalSeq = downloadListRepository.findTopByOrderByIdDesc();
                    long tempId;
                    if (optionalSeq.isPresent()) {
                        Long id = optionalSeq.get().getId() + 100L;                    
                        log.debug("id: " + id);
                        tempId = id;
                    } else {
                        tempId = 100L;
                    }
                    DownloadList download = new DownloadList();
                    download.setUri(rssFeed.getLink());
                    download.setDownloadPath(path);
                    download.setId(tempId);
                    httpDownloadService.createTransmission(download);
                    addToSeenList(rssFeed, path, watchList.getRename());
                }
            } else if (StringUtils.equals(optionalSetting.get().getValue(), "DOWNLOAD_STATION")) {
                // Request Download to Download Station
                if (downloadStationService.create(rssFeed.getLink(), path)) {
                    // Add to Seen
                    addToSeenList(rssFeed, path, watchList.getRename());

                    // Add to Download List
                    boolean isExist = false;
                    for (DownloadList down : downloadStationService.list()) {
                        if (StringUtils.equals(rssFeed.getLink(), down.getUri())) {
                            isExist = true;
                            // downloadListRepository.save(down);
                            addToDownloadList(down, rssFeed, watchList);
                        }
                    }

                    if (isExist == false) {
                        addToDownloadList(0L, rssFeed, watchList, path);
                    }
                }
            }
        }
    }

    private void download(RssFeed rssFeed, RssList rssList) {
        String path = downloadPathRepository.computedPath(rssList.getDownloadPath(), rssFeed.getRssTitle(),
            rssFeed.getRssSeason());
        if (StringUtils.isBlank(path)) {
            path = rssList.getDownloadPath();
        }

        Optional<Setting> optionalSetting = settingRepository.findByKey("DOWNLOAD_APP");
        if (optionalSetting.isPresent()) {
            if (StringUtils.equals(optionalSetting.get().getValue(), "TRANSMISSION")) {
                // Request Download to Transmission
                if (StringUtils.startsWith(rssFeed.getLink(), "magnet")
                    || StringUtils.equalsIgnoreCase(FilenameUtils.getExtension(rssFeed.getLink()), "torrent")) {
                    int torrentAddedId = transmissionService.torrentAdd(rssFeed.getLink(), path);
                    log.debug("Transmission ID: " + torrentAddedId);
    
                    if (torrentAddedId > 0) {
                        // Add to Seen
                        // addToSeenList(rssFeed, path);
    
                        // Add to Download List
                        addToDownloadList((long) torrentAddedId, rssFeed, path);
                    }
                } else {
                    Optional<DownloadList> optionalSeq = downloadListRepository.findTopByOrderByIdDesc();
                    long tempId;
                    if (optionalSeq.isPresent()) {
                        Long id = optionalSeq.get().getId() + 100L;                    
                        log.debug("id: " + id);
                        tempId = id;
                    } else {
                        tempId = 100L;
                    }
                    DownloadList download = new DownloadList();
                    download.setUri(rssFeed.getLink());
                    download.setDownloadPath(path);
                    download.setId(tempId);
                    httpDownloadService.createTransmission(download);
                }
            } else if (StringUtils.equals(optionalSetting.get().getValue(), "DOWNLOAD_STATION")) {
                // Request Download to Download Station
                if (downloadStationService.create(rssFeed.getLink(), path)) {
                    // Add to Seen
                    // addToSeenList(rssFeed, path);

                    // Add to Download List
                    boolean isExist = false;
                    for (DownloadList down : downloadStationService.list()) {
                        if (StringUtils.equals(rssFeed.getLink(), down.getUri())) {
                            isExist = true;
                            downloadListRepository.save(down);
                        }
                    }

                    if (isExist == false) {
                        addToDownloadList(0L, rssFeed, path);
                    }
                }
            }
        }
    }

}