package com.its.wthr.service;

import com.its.app.utils.SysUtils;
import com.its.wthr.config.FrcsConfig;
import com.its.wthr.mapper.FrcsMapper;
import com.its.wthr.process.DbmsJobData;
import com.its.wthr.process.DbmsJobProcess;
import com.its.wthr.process.DbmsJobType;
import com.its.wthr.repository.AppRepository;
import com.its.wthr.rss.Feed;
import com.its.wthr.rss.FeedMessage;
import com.its.wthr.rss.RSSFeedParser;
import com.its.wthr.ui.MainUI;
import com.its.wthr.vo.FrcsZoneSttsVo;
import com.its.wthr.vo.FrcsZoneVo;
import com.rometools.rome.feed.synd.SyndEntry;
import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.io.FeedException;
import com.rometools.rome.io.SyndFeedInput;
import com.rometools.rome.io.XmlReader;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.annotation.PostConstruct;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
@RequiredArgsConstructor
@Service
@Transactional(rollbackFor = {Exception.class})
public class FrcsService extends AbstractXmlService {

    private final FrcsMapper mapper;
    private final FrcsConfig config;
    private final DbmsJobProcess dbmsJobProcess;

    @PostConstruct
    private void postConstruct() {
        log.info("init");
        if (this.config.isEnabled()) {
            load();
        }
    }

    public void load() {
        try {
            for (Map.Entry<String, FrcsZoneVo> e : AppRepository.getInstance().getFrcsZoneMap().entrySet()) {
                e.getValue().setDEL_YN("Y");
            }

            List<FrcsZoneVo> infoList = this.mapper.selectAll();
            for (FrcsZoneVo obj : infoList) {
                AppRepository.getInstance().getFrcsZoneMap().put(obj.getVILG_FRCS_ZONE_CD(), obj);
            }
            for (Map.Entry<String, FrcsZoneVo> e : AppRepository.getInstance().getFrcsZoneMap().entrySet()) {
                e.getValue().setRequestResult(1);
//                log.info("{}", e.getValue());
            }
        }
        catch (Exception e) {
            log.error("{}", e.getMessage());
        }
    }

    public void doJob() {
        if (!this.config.isEnabled()) {
            return;
        }

        String sysTime = SysUtils.getSysTimeStr();
        log.info("FrcsService doJob Start...{}", Thread.currentThread().getName());

        //load();

        int errCnt = 0;
        try {
            log.info("FrcsService.doJob: {} EA", AppRepository.getInstance().getFrcsZoneMap().size());
            for (Map.Entry<String, FrcsZoneVo> e : AppRepository.getInstance().getFrcsZoneMap().entrySet()) {
                FrcsZoneVo obj = e.getValue();
                if (obj.getDEL_YN().equals("Y")) continue;

                //log.info("FrcsService.doJob: {}-{}", obj.getVILG_FRCS_ZONE_CD(), obj.getVILG_FRCS_ZONE_NM());
                try {
                    AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setRequestTime(SysUtils.getSysTimeStr());

                    List<FrcsZoneSttsVo> frcsZoneSttsList = getFrcsInfo(obj.getVILG_FRCS_ZONE_CD(), obj.getGRIDX(), obj.getGRIDY());
                    if (frcsZoneSttsList != null && frcsZoneSttsList.size() > 0) {
                        this.dbmsJobProcess.add(new DbmsJobData(DbmsJobType.DATA_TYPE_FRCS, false, frcsZoneSttsList, frcsZoneSttsList.size()));

                        AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setRequestResult(1);
                        AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setRequestValue(frcsZoneSttsList.get(0).toString());
                        AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setValue(frcsZoneSttsList.get(0).getValue());
                    }
                    else {
                        errCnt++;
                        AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setRequestResult(0);
                        AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setRequestValue("");
                        AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setValue("");
                    }
                }
                catch(Exception e1) {
                    errCnt++;
                    AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setRequestResult(0);
                    AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setRequestValue(e1.toString());
                    AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setValue(e1.toString());
                    log.error("FrcsService.doJob: {}: {}, Exception: {}", obj.getVILG_FRCS_ZONE_CD(), obj.getVILG_FRCS_ZONE_NM(), e1.toString());
                }
            }
        }
        catch(Exception e) {
            log.error("FrcsService.doJob: {}", e.toString());
        }

        //////////////
        ////////////// 네트워크 또는 기상청 서버 오류로 인한 오류가 발생했을 경우 재시도
        //////////////
        if (errCnt > 0) {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                log.error("FrcsService InterruptedException thread sleep exception");
            }

            for (Map.Entry<String, FrcsZoneVo> e : AppRepository.getInstance().getFrcsZoneMap().entrySet()) {
                FrcsZoneVo obj = e.getValue();
                if (obj.getRequestResult() == 1) {
                    continue;
                }

                log.info("FrcsService.doJob-retry: {}-{}", obj.getVILG_FRCS_ZONE_CD(), obj.getVILG_FRCS_ZONE_NM());
                try {
                    AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setRequestTime(SysUtils.getSysTimeStr());

                    List<FrcsZoneSttsVo> frcsZoneSttsList = getFrcsInfo(obj.getVILG_FRCS_ZONE_CD(), obj.getGRIDX(), obj.getGRIDY());
                    if (frcsZoneSttsList != null && frcsZoneSttsList.size() > 0) {
                        this.dbmsJobProcess.add(new DbmsJobData(DbmsJobType.DATA_TYPE_FRCS, false, frcsZoneSttsList, frcsZoneSttsList.size()));

                        AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setRequestResult(1);
                        AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setRequestValue(frcsZoneSttsList.get(0).toString());
                        AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setValue(frcsZoneSttsList.get(0).getValue());
                    }
                    else {
                        errCnt++;
                        AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setRequestResult(0);
                        AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setRequestValue("");
                        AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setValue("");
                    }
                } catch (Exception e1) {
                    errCnt++;
                    AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setRequestResult(0);
                    AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setRequestValue(e.toString());
                    AppRepository.getInstance().getFrcsZoneMap().get(obj.getVILG_FRCS_ZONE_CD()).setValue(e.toString());
                    log.error("FrcsService.doJob-retry: {}: {}, Exception: {}", obj.getVILG_FRCS_ZONE_CD(), obj.getVILG_FRCS_ZONE_NM(), e1.toString());
                }
            }
        }
        log.info("FrcsService doJob ..End...");

        MainUI UI = MainUI.getInstance();
        if (UI != null) {
            UI.updateFrcsPltnStts(sysTime);
        }
    }

    List<FrcsZoneSttsVo> getFrcsInfo(String frcsZoneCode, String GRIDX, String GRIDY) {
        List<FrcsZoneSttsVo> sttsList = new ArrayList<FrcsZoneSttsVo>();
        String url = "https://www.kma.go.kr/wid/queryDFS.jsp?gridx=" + GRIDX + "&gridy=" + GRIDY;
        log.info("FrcsService.getFrcsInfo: {}, {}", frcsZoneCode, url);

        NodeList list = getXmlNodeList(url, "wid");
        if (list == null || list.getLength() == 0) {
            log.error("FrcsService.getFrcsInfo: nodeList null");
            return null;
        }
        log.info("FrcsService.getFrcsInfo: {}, nodeList: {} EA.", frcsZoneCode, list.getLength());
        try {
            Node wid = list.item(0);    // <wid> 하나만 존재함
            NodeList widList = wid.getChildNodes();
            //log.info("widList: {}, {}, {} EA", wid.getNodeType(), wid.getNodeName(), widList.getLength());
            String tm = "";
            for (int ii = 0; ii < widList.getLength(); ii++) {
                Node node = widList.item(ii);
                String tag = node.getNodeName();
                if (tag.equals("header")) {
                    Element header = (Element)node;
                    tm = header.getElementsByTagName("tm").item(0).getTextContent().trim();
                    String ts = header.getElementsByTagName("ts").item(0).getTextContent().trim();
                    String x = header.getElementsByTagName("x").item(0).getTextContent().trim();
                    String y = header.getElementsByTagName("y").item(0).getTextContent().trim();

                    //log.info("header: {}, {}, {}, {}", tm, ts, x, y);
                }
                else if (tag.equals("body")) {
                    NodeList body = node.getChildNodes();
                    if (tm.equals("")) {
                        log.error("not set header: {}", frcsZoneCode);
                        continue;
                    }
                    //log.error("body: {} EA.", body.getLength());
                    for (int jj = 0; jj < body.getLength(); jj++) {
                        Node dataBody = body.item(jj);
                        if (dataBody.getNodeType() != 1) {
                            continue;
                        }
                        Element data = (Element)dataBody;

                        String tmp;
                        String order = data.getAttributes().item(0).getTextContent().trim();
                        FrcsZoneSttsVo stts = new FrcsZoneSttsVo();
                        stts.setVILG_FRCS_ZONE_CD(frcsZoneCode);
                        stts.setANNC_DT(tm+"00");
                        stts.setORD(order);
                        tmp = data.getElementsByTagName("hour").item(0).getTextContent();   if (tmp != null) stts.setHH(tmp.trim());
                        tmp = data.getElementsByTagName("day").item(0).getTextContent();    if (tmp != null) stts.setDD(tmp.trim());
                        tmp = data.getElementsByTagName("temp").item(0).getTextContent();   if (tmp != null) stts.setPRST_TMPR(tmp.trim());
                        tmp = data.getElementsByTagName("tmx").item(0).getTextContent();    if (tmp != null) stts.setHGHS_TMPR(tmp.trim());
                        tmp = data.getElementsByTagName("tmn").item(0).getTextContent();    if (tmp != null) stts.setLWST_TMPR(tmp.trim());
                        tmp = data.getElementsByTagName("sky").item(0).getTextContent();    if (tmp != null) stts.setATMP_STTS(tmp.trim());
                        tmp = data.getElementsByTagName("pty").item(0).getTextContent();    if (tmp != null) stts.setPRCP_STTS(tmp.trim());
                        tmp = data.getElementsByTagName("wfKor").item(0).getTextContent();  if (tmp != null) stts.setWTCD_KOR_NM(tmp.trim());
                        tmp = data.getElementsByTagName("wfEn").item(0).getTextContent();   if (tmp != null) stts.setWTCD_ENGL_NM(tmp.trim());
                        tmp = data.getElementsByTagName("pop").item(0).getTextContent();    if (tmp != null) stts.setPRCP_PR(tmp.trim());
                        tmp = data.getElementsByTagName("r12").item(0).getTextContent();    if (tmp != null) stts.setESTM_PRAM_12HH(tmp.trim());
                        tmp = data.getElementsByTagName("s12").item(0).getTextContent();    if (tmp != null) stts.setESTM_SNOW_AMUT_12HH(tmp.trim());
                        tmp = data.getElementsByTagName("ws").item(0).getTextContent();     if (tmp != null) stts.setWNSP(tmp.trim());
                        tmp = data.getElementsByTagName("wd").item(0).getTextContent();     if (tmp != null) stts.setWNDR(tmp.trim());
                        tmp = data.getElementsByTagName("wdKor").item(0).getTextContent();  if (tmp != null) stts.setWNDR_KOR_NM(tmp.trim());
                        tmp = data.getElementsByTagName("wdEn").item(0).getTextContent();   if (tmp != null) stts.setWNDR_ENGL_NM(tmp.trim());
                        tmp = data.getElementsByTagName("reh").item(0).getTextContent();    if (tmp != null) stts.setHMDT(tmp.trim());
                        tmp = data.getElementsByTagName("r06").item(0).getTextContent();    if (tmp != null) stts.setESTM_PRCP_AMUT_6HH(tmp.trim());
                        tmp = data.getElementsByTagName("s06").item(0).getTextContent();    if (tmp != null) stts.setESTM_SNOW_AMUT_6HH(tmp.trim());

                        sttsList.add(stts);
                        //log.debug("{}, seq: {}, {}", jj, data.getAttributes().item(0).getTextContent(), stts.toString());
                    }
                }
            }

        }
        catch(Exception e) {
            log.error("{}", e.toString());
        }
        return sttsList;
    }

    List<FrcsZoneSttsVo> getFrcsInfoRSS(String frcsZoneCode) {
        List<FrcsZoneSttsVo> sttsList = new ArrayList<FrcsZoneSttsVo>();
        String url = "http://www.kma.go.kr/wid/queryDFSRSS.jsp?zone=" + frcsZoneCode;
        log.info("FrcsService.getFrcsInfoRSS: {}, {}", frcsZoneCode, url);
        //https://www.kma.go.kr/wid/queryDFS.jsp?gridx=84&gridy=96

        getRSS(url);
        //getXmlString(url);

        RSSFeedParser parser = null;
        try {
            parser = new RSSFeedParser(url);
            Feed feed = parser.readFeed();
            for (FeedMessage message : feed.getMessages()) {
                log.error("FeedMessage: {}, {}", frcsZoneCode, message);
            }
        }
        catch(Exception e) {
            log.error("FrcsService.getFrcsInfo: {}", e.toString());
        }
        return null;
    }

    public String getRSS(String strUrl) {
        SyndFeedInput input = new SyndFeedInput();
        log.debug("getRSS URL: {}", strUrl);
        try {
            URL feedSource = new URL(strUrl);
            SyndFeed syndFeed = input.build(new XmlReader(feedSource));
            List<SyndEntry> entries = syndFeed.getEntries();
            int i = 0;
            SyndEntry syndEntry = null;
            for (int j = entries.size(); i < j; i++)
            {
                syndEntry = (SyndEntry)entries.get(i);
                log.error("{}, {}, {}", strUrl, i, syndEntry.toString());
            }
        }
        catch (IOException ex) {
            System.out.println("IO exception occurred due to: "+ ex);
        }
        catch (FeedException ex) {
            System.out.println("Feed exception occurred due to: "+ ex);
        }

        return "";
    }

    public String getXmlString(String stringUrl) {
        StringBuilder urlBuilder = new StringBuilder(stringUrl);
        URL url = null;
        try {
            url = new URL(urlBuilder.toString());
        } catch (MalformedURLException e) {
            log.error("getXmlString MalformedURLException exception");
        }

        HttpURLConnection conn = null;
        try {
            conn = (HttpURLConnection) url.openConnection();
        } catch (IOException e) {
            log.error("getXmlString IOException");
        }

        try {
            conn.setRequestMethod("GET");
        } catch (ProtocolException e) {
            log.error("getXmlString ProtocolException");
        }
        conn.setRequestProperty("Content-type", "application/json");

        BufferedReader rd;
        StringBuilder sb = new StringBuilder();
        try {
            String line;
            if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
                rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            } else {
                rd = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
            }
            while ((line = rd.readLine()) != null) {
                sb.append(line);
            }
            rd.close();
        } catch (IOException e) {
            log.error("getXmlString IOException");
        }
        conn.disconnect();
        log.error(sb.toString());
        return sb.toString();
    }
}
