package ru.yandex.wmtools.common.data.xmlsearch;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ru.yandex.wmtools.common.error.InternalException;
import ru.yandex.wmtools.common.error.UserException;
import ru.yandex.wmtools.common.error.UserProblem;
import ru.yandex.wmtools.common.servantlet.AbstractServantlet;
import ru.yandex.wmtools.common.util.TimeFilter;

/**
 * Created by IntelliJ IDEA.
 * User: senin
 * Date: 07.12.2007
 * Time: 23:57:52
 */
public abstract class HostPathRequest extends AbstractRequest {
    private static final Logger log = LoggerFactory.getLogger(HostPathRequest.class);
    public static final String WWW_PREFIX = "www.";
    public static final String PROTOCOL_SIGNS = "://";
    private static final String DEFAULT_SCHEME = "http";
    public static final int MAX_REQUEST_LENGTH = 400; // TODO: this limit is much more bigger if we use long-request=1 cgi parameter

    private static final int MAX_DOC_NUM = 1000;
    private static final int MAX_PAGE_SIZE = 100;

    private final int pageNum;
    private final int pageSize;
    private String query;

    private final String hostnameWithoutProtocol;
    private final String hostnameWithProtocolIfAny;
    private final String scheme;    //todo remove?
    private final String path;
    private final TimeFilter timeFilter;

    public HostPathRequest(String hostname, String path, TimeFilter timeFilter, int pageNum, int pageSize)
            throws UserException {
        /* NB! we limit page size and page num in order to limit requests to xml search */
        this.pageSize = Math.max(1, Math.min(pageSize, MAX_PAGE_SIZE));
        // assertion: 1 <= pageSize <= MAX_PAGE_SIZE
        this.pageNum = Math.max(0, Math.min(pageNum, (MAX_DOC_NUM / this.pageSize) - 1));
        // assertion: 0 <= pageNum <= (MAX_DOC_NUM / this.pageSize) - 1
        this.path = path;
        this.timeFilter = timeFilter;

        hostnameWithProtocolIfAny = hostname;
        if (hostname.contains(PROTOCOL_SIGNS)) {
            int index = hostname.indexOf(PROTOCOL_SIGNS);
            this.hostnameWithoutProtocol = hostname.toLowerCase().substring(index + PROTOCOL_SIGNS.length());
            this.scheme = hostname.toLowerCase().substring(0, index);
        } else {
            this.hostnameWithoutProtocol = hostname.toLowerCase();
            this.scheme = DEFAULT_SCHEME;
        }

        if (!isValid(getHostname(), path)) {
            throw new UserException(UserProblem.INVALID_PATH, "HostPathRequest", "Invalid path: " + path);
        }
    }

    public HostPathRequest(String hostname, String path, int pageNum, int pageSize) throws UserException {
        this(hostname, path, TimeFilter.createNull(), pageNum, pageSize);
    }

    @Override
    protected int getPageNum() {
        return pageNum;
    }

    @Override
    protected int getPageSize() {
        return pageSize;
    }

    protected String getPath() {
        return path;
    }

    @Override
    protected String getQueryText() throws InternalException {
        if (query == null) {
            query = buildXmlSearchQuery();
        }
        return query;
    }

    protected String buildXmlSearchQuery() throws InternalException {
        String hostname = getHostname();

        String withOrWithoutWWW = hostname.startsWith(WWW_PREFIX) ? hostname.substring(4) : (WWW_PREFIX + hostname);
        String rOwner1 = getOwnerReversed(hostname.toLowerCase());
        String rOwner2 = getOwnerReversed(withOrWithoutWWW.toLowerCase());
        String queryStr = ("*".equals(path)) ? buildWholeHostQuery(rOwner1, rOwner2) : buildQuery(rOwner1, rOwner2);

        if (timeFilter.getFromInMilliseconds() != null) {
            queryStr = "(" + queryStr + ")" + " idate:>" + String.format("%1$tY%1$tm%1$td", new Date(timeFilter.getFromInMilliseconds())) + "";
        }

        // TODO �����! ����� �������� �� ��������, ������� �� �� ���������. ������ ��� ��������
        // TODO � XML-������ ������ �������� ��� ���� ���������� �����

//        if (separateProtocolAsScheme() && (scheme != null)) {
//            queryStr += " << scheme=\"" + scheme + "\"";
//        }

        log.debug("Query: '" + queryStr + "'");
        log.debug("Params: '" + getUrlParams() + "'");
        return queryStr;
    }

    private boolean isValid(String hostname, String path) {
        if (path.length() == 0) {
            return true;
        }
        if ("*".equals(path)) {
            return true;
        }
        if (path.charAt(0) != '/') {
            return false;
        }
        if (path.charAt(path.length() - 1) == '*') {
            path = path.substring(0, path.length() - 1);
        }
        try {
            final URL url;
            if (hostname.startsWith(scheme)) {
                url = new URL(hostname + path);
            } else {
                url = new URL(scheme + PROTOCOL_SIGNS + hostname + path);
            }
            if (!AbstractServantlet.isValid(url)) {
                return false;
            }
        } catch (MalformedURLException e) {
            return false;
        }

        return true;
    }

    public static int getMaxDocNum() {
        return MAX_DOC_NUM;
    }

    protected final String getHostname() {
        return (separateProtocolAsScheme()) ? hostnameWithoutProtocol : hostnameWithProtocolIfAny;
    }

    protected abstract boolean separateProtocolAsScheme();

    // TODO: refactoring needed
    protected abstract String buildQuery(String rOwner1, String rOwner2);

    protected abstract String buildWholeHostQuery(String rOwner1, String rOwner2);
}
