package ru.yandex.wmconsole.verification;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;

import org.apache.http.Consts;
import org.cyberneko.html.parsers.SAXParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import ru.yandex.wmconsole.data.VerificationStateEnum;
import ru.yandex.wmconsole.data.info.UsersHostsInfo;
import ru.yandex.wmtools.common.SupportedProtocols;
import ru.yandex.wmtools.common.sita.DocumentFormatEnum;
import ru.yandex.wmtools.common.sita.SitaException;
import ru.yandex.wmtools.common.sita.SitaRequestTimeout;
import ru.yandex.wmtools.common.sita.SitaService;
import ru.yandex.wmtools.common.sita.SitaUrlFetchRequest;
import ru.yandex.wmtools.common.sita.SitaUrlFetchRequestBuilder;
import ru.yandex.wmtools.common.sita.SitaUrlFetchResponse;
import ru.yandex.wmtools.common.sita.UserAgentEnum;
import ru.yandex.wmtools.common.util.http.YandexHttpStatus;

/**
 * @author baton
 */
public class HtmlFileVerifier extends ConsistentVerifier {
    private static final Logger log = LoggerFactory.getLogger(HtmlFileVerifier.class);

    private int socketTimeoutMillis = 10000;

    private SitaService sitaNoCheckService;

    @Override
    public UsersHostsInfo verify(UsersHostsInfo verInfo) {
        URL urlAddress = getHtmlFileVerificationAddress(verInfo.getHostName(), verInfo.getVerificationUin());
        log.debug("Verifying " + urlAddress + "...");

        CheckHtmlFileContentHandler checkHtmlFileContentHandler = new CheckHtmlFileContentHandler(
                getHtmlFileVerificationContent(verInfo.getVerificationUin()));
        try {
            SitaUrlFetchRequest sitaUrlFetchRequest = new SitaUrlFetchRequestBuilder(urlAddress)
                    .setUserAgent(UserAgentEnum.WEBMASTER)
                    .setCheckAllowedInRobotsTxt(false)
                    .setDocumentFormat(DocumentFormatEnum.DF_HTTP_RESPONSE)
                    .setRequestTimeout(SitaRequestTimeout._15_SECONDS)
                    .createSitaUrlFetchRequest();
            sitaUrlFetchRequest.setSitaSocketTimeoutMillis(socketTimeoutMillis);
            SitaUrlFetchResponse sitaUrlFetchResponse = sitaNoCheckService.request(sitaUrlFetchRequest);
            YandexHttpStatus sitaHttpStatus = sitaUrlFetchResponse.getSitaHttpStatus();
            if (YandexHttpStatus.isStandardHttpCode(sitaHttpStatus)
                    && sitaHttpStatus != YandexHttpStatus.HTTP_200_OK) {
                String verifyFaultLog = "SAX. Checking html-file: Not verified: Bad http code: " +
                        sitaHttpStatus.getCode() + " for " + urlAddress;
                log.debug(verifyFaultLog);
                return UsersHostsInfo.createNotVerifiedNow(verInfo, VerificationStateEnum.HTML_FILE_IO_EXCEPTION, verifyFaultLog, sitaHttpStatus.getCode());
            }

            SAXParser saxParser = new SAXParser();
            saxParser.setFeature("http://cyberneko.org/html/features/balance-tags", false);
            saxParser.setContentHandler(checkHtmlFileContentHandler);
            saxParser.setErrorHandler(checkHtmlFileContentHandler);
            saxParser.parse(new InputSource(sitaUrlFetchResponse.getDocumentContent(Consts.ISO_8859_1)));
        } catch (IOException e) {
            if (!checkHtmlFileContentHandler.isFound()) {
                String verifyFaultLog = "SAX. Checking html-file: Not verified: IOException while reading file: " + urlAddress + "\n" + e;
                log.debug(verifyFaultLog);
                return UsersHostsInfo.createNotVerifiedNow(verInfo, VerificationStateEnum.HTML_FILE_IO_EXCEPTION, verifyFaultLog);
            }
        } catch(SitaException e) {
            String verifyFaultLog = "Unable to download " + urlAddress;
            log.debug(verifyFaultLog, e);
            return UsersHostsInfo.createNotVerifiedNow(verInfo, VerificationStateEnum.HTML_FILE_IO_EXCEPTION, verifyFaultLog);
        } catch (SAXException e) {
            if (!checkHtmlFileContentHandler.isFound()) {
                String verifyFaultLog = "SAX. Checking html-file: Not verified: SAXException while reading file: " + urlAddress + "\n" + e;
                log.debug(verifyFaultLog);
                return UsersHostsInfo.createNotVerifiedNow(verInfo, VerificationStateEnum.HTML_FILE_PARSER_EXCEPTION, verifyFaultLog);
            }
        } catch (Throwable e) {
            if ((checkHtmlFileContentHandler == null) || (!checkHtmlFileContentHandler.isFound())) {
                String verifyFaultLog = "SAX. Checking html-file: Not verified: Throwable thrown while reading file: " + urlAddress + "\n" + e;
                log.debug(verifyFaultLog);
                return UsersHostsInfo.createNotVerifiedNow(verInfo, VerificationStateEnum.HTML_FILE_PARSER_EXCEPTION, verifyFaultLog);
            }
        }

        if (checkHtmlFileContentHandler.isFound()) {
            return UsersHostsInfo.createVerifiedNow(verInfo);
        } else {
            return UsersHostsInfo.createNotVerifiedNow(verInfo, VerificationStateEnum.HTML_FILE_WRONG_CONTENT, checkHtmlFileContentHandler.getFaultLog());
        }
    }

    private URL getHtmlFileVerificationAddress(String hostname, long verificationUin) {
        try {
            String url = String.format("%1$s/yandex_%2$s.html",
                    hostname.toLowerCase(), VerificationIdentifier.valueOf(verificationUin).toString());
            return SupportedProtocols.getURL(url);
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("invalid hostname has been passed", e);
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException("invalid hostname has been passed", e);
        } catch (SupportedProtocols.UnsupportedProtocolException e) {
            throw new IllegalArgumentException("hostname with unsupported protocol has been passed", e);
        }
    }

    private String getHtmlFileVerificationContent(long verificationUin) {
        return "Verification: " + VerificationIdentifier.valueOf(verificationUin).toString();
    }

    @Required
    public void setSitaNoCheckService(SitaService sitaService) {
        this.sitaNoCheckService = sitaService;
    }

//    private class NarodContentFilter extends InputStream {
//
//        private final BufferedReader in;
//        private byte[] buffer;
//        private int offset = 0;
//        private boolean skipMetrics = false;
//
//        public NarodContentFilter(InputStream is) {
//            in = new BufferedReader(new InputStreamReader(is, Charset.forName("utf-8")));
//        }
//
//        @Override
//        public int read() throws IOException {
//
//            if (buffer == null || offset >= buffer.length) {
//                String s = in.readLine();
//                if (s == null) {
//                    return -1;
//                }
//
//                if (!skipMetrics && s.trim().equals("<!-- Yandex.Metrika counter -->")) {
//                    while ((s = in.readLine()) != null && !s.trim().equals("<!-- /Yandex.Metrika counter -->")) ;
//
//                    s = in.readLine();
//
//                    if (s == null) {
//                        return -1;
//                    }
//                    skipMetrics = true;
//                }
//                buffer = (s + "\n").getBytes(Charset.forName("utf-8"));
//                offset = 0;
//            }
//
//            return buffer[offset++];
//        }
//    }
}
