package ru.yandex.webmaster3.viewer;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.io.Resource;
import ru.yandex.webmaster3.viewer.http.camelcase.data.BeautyHostName;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * Partial copy of CheckHostNameService
 *
 * @author aherman
 */
public class W3CheckHostNameService {
    public enum DisplayNameCheckResult {
        GOOD,
        NEEDS_MODERATION,

        ERROR_TOO_LITTLE_DOMAIN_NAME_PARTS,
        ERROR_TLD_CASE_CHANGE,
        ERROR_TOO_MANY_CONSECUTIVE_UPPER_LETTERS,
        ERROR_WRONG_GEO_NAME_CASE,
        ERROR_WRONG_WWW_CASE,
    }

    private Collection<String> geoList;
    private Collection<String> ibmList;
    private Collection<String> townList;
    private final Collection<String> wwwList = Arrays.asList("www", "w3", "ftp");

    private Resource geoListResource;
    private Resource ibmListResource;
    private Resource townListResource;

    public void init() throws IOException {
        InputStream geoListIS = geoListResource.getInputStream();
        List<String> tmpGeoList;
        try {
            tmpGeoList = new ArrayList<>(IOUtils.readLines(geoListIS));
        } finally {
            IOUtils.closeQuietly(geoListIS);
        }

        InputStream ibmListIS = ibmListResource.getInputStream();
        List<String> tmpIbmList;
        try {
            tmpIbmList = new ArrayList<>(IOUtils.readLines(ibmListIS));
        } finally {
            IOUtils.closeQuietly(ibmListIS);
        }

        InputStream townListIS = townListResource.getInputStream();
        List<String> tmpTownList;
        try {
            tmpTownList = new ArrayList<>(IOUtils.readLines(townListIS));
        } finally {
            IOUtils.closeQuietly(townListIS);
        }

        geoList = tmpGeoList;
        ibmList = tmpIbmList;
        townList = tmpTownList;
    }

    public BeautyHostName getDisplayName(String displayName) {
        final String [] tokens = displayName.split("\\.");
        if (tokens.length < 2) {
            return new BeautyHostName(null, displayName, null);
        }

        final String firstSubdomainPart = tokens[0];
        String tldPart = tokens[tokens.length - 1]; // Ex: ru
        String ownerPart = tokens[tokens.length - 2] + "." + tokens[tokens.length - 1]; // Ex: yandex.ru

        // First token is considered as prefix only if domain has at least two parts and is in www list
        final String prefix;
        if (tokens.length > 2 && wwwList.contains(firstSubdomainPart.toLowerCase())) {
            prefix = firstSubdomainPart.toLowerCase();
        } else {
            prefix = null;
        }

        // Two last tokens are considered as suffix iff ownerPart is in geo list
        // otherwise only last token is considered as suffix
        final String suffix;
        if (geoList.contains(ownerPart.toLowerCase())) {
            suffix = ownerPart.toLowerCase();
        } else {
            suffix = tldPart.toLowerCase();
        }

        // Main part exists only if full displayname greater than suffix
        // otherwise main part is null and is not editable.
        int startOfMain = prefix != null ? prefix.length() + 1 : 0;
        final String main;
        if (displayName.length() > suffix.length()) {
            main = displayName.substring(startOfMain, displayName.length() - suffix.length() - 1);
        } else {
            main = null;
        }
        return new BeautyHostName(prefix, main, suffix);
    }

    public DisplayNameCheckResult checkDisplayName(String displayName) {
        boolean semi = false;

        String[] toks = displayName.split("\\.");
        if (toks.length < 2) {
            return DisplayNameCheckResult.ERROR_TOO_LITTLE_DOMAIN_NAME_PARTS;
        }

        String firstSubdomainPart = toks[0]; // Ex: www
        String tldPart = toks[toks.length - 1]; // Ex: ru
        String ownerPart = toks[toks.length - 2] + "." + toks[toks.length - 1]; // Ex: yandex.ru

        if (wwwList.contains(firstSubdomainPart.toLowerCase()) && !firstSubdomainPart.equals(firstSubdomainPart.toLowerCase()) &&
                toks.length > 2) {
            return DisplayNameCheckResult.ERROR_WRONG_WWW_CASE;
        }

        if  (!tldPart.equals(tldPart.toLowerCase())) {
            return DisplayNameCheckResult.ERROR_TLD_CASE_CHANGE;
        }

        if (geoList.contains(ownerPart.toLowerCase())) {
            if (!ownerPart.equals(ownerPart.toLowerCase())) {
                return DisplayNameCheckResult.ERROR_WRONG_GEO_NAME_CASE;
            }
        }

        for (String tok : toks) {
            if (tok.equals(tok.toLowerCase())) {
                continue;
            }
            if (ibmList.contains(tok)) {
                continue;
            }
            if (townList.contains(tok)) {
                continue;
            }

            UpperCount x = getUpperCount(tok);
            if (x.maxConsecutiveUpperCount > 5) {
                return DisplayNameCheckResult.ERROR_TOO_MANY_CONSECUTIVE_UPPER_LETTERS;
            }

            if (x.totalUpperCount == 2 && twoWordsString(tok) && !tok.contains("-")) {
                continue;
            }

            semi = true;
        }

        if (semi) {
            return DisplayNameCheckResult.NEEDS_MODERATION;
        } else {
            return DisplayNameCheckResult.GOOD;
        }
    }

    private static UpperCount getUpperCount(String tok) {
        String ltok = tok.toLowerCase();
        int k = 0;
        int m = 0;
        int mm = 0;
        for (int i = 0; i < tok.length(); i++) {
            if (tok.charAt(i) != ltok.charAt(i)) {
                k++;
                m++;
            } else {
                mm = Math.max(mm, m);
                m = 0;
            }
        }
        mm = Math.max(mm, m);
        return new UpperCount(k, mm);
    }

    private boolean twoWordsString(String tok) {
        String ltok = tok.toLowerCase();
        if (tok.charAt(0) != ltok.charAt(0)) {
            for (int i = 3; i < tok.length() - 2; i++) {
                if (tok.charAt(i) != ltok.charAt(i)) {
                    return true;
                }
            }
        }
        return false;
    }

    private static class UpperCount {
        private final int totalUpperCount;
        private final int maxConsecutiveUpperCount;

        private UpperCount(int totalUpperCount, int maxConsecutiveUpperCount) {
            this.totalUpperCount = totalUpperCount;
            this.maxConsecutiveUpperCount = maxConsecutiveUpperCount;
        }
    }

    @Required
    public void setGeoListResource(Resource geoListResource) {
        this.geoListResource = geoListResource;
    }

    @Required
    public void setIbmListResource(Resource ibmListResource) {
        this.ibmListResource = ibmListResource;
    }

    @Required
    public void setTownListResource(Resource townListResource) {
        this.townListResource = townListResource;
    }

}