package ru.yandex.wmconsole.viewer.api;

import java.net.URI;
import java.net.URISyntaxException;

import org.springframework.beans.factory.annotation.Required;

import ru.yandex.wmconsole.viewer.sitemap.SitemapId;

/**
 * @author aherman
 */
public class ApiUrl {
    private URI apiV1BaseUri;
    private URI apiV2BaseUri;
    private URI apiV3BaseUri;

    public V1 v1() {
        return new V1();
    }

    public class V1 {
        private URI createURI(StringBuilder sb) {
            return ApiUrl.appendPath(apiV1BaseUri, sb);
        }

        public V1.User user(long userId) {
            return new User(userId);
        }

        public class User {
            private final long userId;

            private User(long userId) {
                this.userId = userId;
            }

            public Host hosts(long hostId) {
                return new Host(hostId);
            }

            protected void appendUserPart(StringBuilder sb) {
                sb.append('/').append(userId);
            }

            public class Host {
                private final long hostId;

                private Host(long hostId) {
                    this.hostId = hostId;
                }

                protected void appendHostPart(StringBuilder sb) {
                    sb.append("/hosts/").append(hostId);
                }

                private void appendUserAndHost(StringBuilder sb) {
                    appendUserPart(sb);
                    appendHostPart(sb);
                }

                public ApiLink stats() {
                    StringBuilder sb = new StringBuilder();
                    appendUserAndHost(sb);
                    sb.append("/stats");
                    URI uri = createURI(sb);
                    return new ApiLink(uri, ApiLinkType.UNKNOWN);
                }

                public ApiLink verify() {
                    StringBuilder sb = new StringBuilder();
                    appendUserAndHost(sb);
                    sb.append("/verify");
                    URI uri = createURI(sb);
                    return new ApiLink(uri, ApiLinkType.UNKNOWN);
                }

                public ApiLink excluded() {
                    StringBuilder sb = new StringBuilder();
                    appendUserAndHost(sb);
                    sb.append("/excluded");
                    URI uri = createURI(sb);
                    return new ApiLink(uri, ApiLinkType.UNKNOWN);
                }

                public ApiLink indexed() {
                    StringBuilder sb = new StringBuilder();
                    appendUserAndHost(sb);
                    sb.append("/indexed");
                    URI uri = createURI(sb);
                    return new ApiLink(uri, ApiLinkType.UNKNOWN);
                }

                public ApiLink links() {
                    StringBuilder sb = new StringBuilder();
                    appendUserAndHost(sb);
                    sb.append("/links");
                    URI uri = createURI(sb);
                    return new ApiLink(uri, ApiLinkType.UNKNOWN);
                }

                public ApiLink tops() {
                    StringBuilder sb = new StringBuilder();
                    appendUserAndHost(sb);
                    sb.append("/tops");
                    URI uri = createURI(sb);
                    return new ApiLink(uri, ApiLinkType.UNKNOWN);
                }
            }
        }
    }

    public V2 v2() {
        return new V2();
    }

    public final class V2 extends AbstractVersion {

        @Override
        protected URI createURI(StringBuilder sb) {
            return ApiUrl.appendPath(apiV2BaseUri, sb);
        }
    }

    public V3 v3() {
        return new V3();
    }

    public class V3 extends AbstractVersion {

        @Override
        protected URI createURI(StringBuilder sb) {
            return ApiUrl.appendPath(apiV3BaseUri, sb);
        }
    }

    public abstract class AbstractVersion {

        abstract protected URI createURI(StringBuilder sb);

        public Host host(long hostId) {
            return new Host(hostId);
        }

        public class Host {
            private final long hostId;

            public Host(long hostId) {
                this.hostId = hostId;
            }

            protected void addHost(StringBuilder sb) {
                sb.append("/hosts/").append(hostId);
            }

            public ApiLink sitemaps(){
                StringBuilder sb = new StringBuilder();
                addHost(sb);
                sb.append("/sitemaps/");
                URI uri = createURI(sb);
                return new ApiLink(uri, ApiLinkType.SITEMAPS);
            }

            public ApiLink sitemap(SitemapId sitemapId) {
                StringBuilder sb = new StringBuilder();
                addHost(sb);
                sb.append("/sitemaps/");
                SitemapIdApiFormatter.toExternalId(sb, sitemapId);
                URI uri = createURI(sb);
                return new ApiLink(uri, ApiLinkType.SITEMAP);
            }

            public ApiLink sitemapChildrens(SitemapId sitemapId) {
                StringBuilder sb = new StringBuilder();
                addHost(sb);
                sb.append("/sitemaps/");
                SitemapIdApiFormatter.toExternalId(sb, sitemapId);
                sb.append("/children/");
                URI uri = createURI(sb);
                return new ApiLink(uri, ApiLinkType.SITEMAP_INDEX_CHILDREN);
            }

            public ApiLink originalTexts() {
                StringBuilder sb = new StringBuilder();
                addHost(sb);
                sb.append("/original-texts/");
                URI uri = createURI(sb);
                return new ApiLink(uri, ApiLinkType.ORIGINAL_TEXTS);
            }

            public ApiLink originalText(String textId) {
                StringBuilder sb = new StringBuilder();
                addHost(sb);
                sb.append("/original-texts/");
                sb.append(textId);
                URI uri = createURI(sb);
                return new ApiLink(uri, ApiLinkType.ORIGINAL_TEXT);
            }

            public ApiLink stats() {
                StringBuilder sb = new StringBuilder();
                addHost(sb);
                sb.append("/stats");
                URI uri = createURI(sb);
                return new ApiLink(uri, ApiLinkType.HOST_INFORMATION);
            }

            public ApiLink verify() {
                StringBuilder sb = new StringBuilder();
                addHost(sb);
                sb.append("/verify");
                URI uri = createURI(sb);
                return new ApiLink(uri, ApiLinkType.VERIFY_HOST);
            }

            public ApiLink excluded() {
                StringBuilder sb = new StringBuilder();
                addHost(sb);
                sb.append("/excluded");
                URI uri = createURI(sb);
                return new ApiLink(uri, ApiLinkType.EXCLUDED_URLS);
            }

            public ApiLink indexed() {
                StringBuilder sb = new StringBuilder();
                addHost(sb);
                sb.append("/indexed");
                URI uri = createURI(sb);
                return new ApiLink(uri, ApiLinkType.INDEXED_URLS);
            }

            public ApiLink links() {
                StringBuilder sb = new StringBuilder();
                addHost(sb);
                sb.append("/links");
                URI uri = createURI(sb);
                return new ApiLink(uri, ApiLinkType.INCOMING_LINKS);
            }

            public ApiLink tops() {
                StringBuilder sb = new StringBuilder();
                addHost(sb);
                sb.append("/tops");
                URI uri = createURI(sb);
                return new ApiLink(uri, ApiLinkType.TOP_QUERIES);
            }

            public History history() {
                return new History();
            }

            public class History {
                protected void addHostHistory(StringBuilder sb) {
                    addHost(sb);
                    sb.append("/history");
                }

                public ApiLink tic() {
                    StringBuilder sb = new StringBuilder();
                    addHostHistory(sb);
                    sb.append("/tic");
                    URI uri = createURI(sb);
                    return new ApiLink(uri, ApiLinkType.TIC_HISTORY);
                }

                public ApiLink incomingLinks() {
                    StringBuilder sb = new StringBuilder();
                    addHostHistory(sb);
                    sb.append("/incoming-links");
                    URI uri = createURI(sb);
                    return new ApiLink(uri, ApiLinkType.INCOMING_LINKS_HISTORY);
                }

                public ApiLink crawledUris() {
                    StringBuilder sb = new StringBuilder();
                    addHostHistory(sb);
                    sb.append("/crawled-urls");
                    URI uri = createURI(sb);
                    return new ApiLink(uri, ApiLinkType.CRAWLED_URLS_HISTORY);
                }

                public ApiLink indexedUrisl() {
                    StringBuilder sb = new StringBuilder();
                    addHostHistory(sb);
                    sb.append("/indexed-urls");
                    URI uri = createURI(sb);
                    return new ApiLink(uri, ApiLinkType.INDEXED_URLS_HISTORY);
                }

                public ApiLink excludedUrls() {
                    StringBuilder sb = new StringBuilder();
                    addHostHistory(sb);
                    sb.append("/excluded-urls");
                    URI uri = createURI(sb);
                    return new ApiLink(uri, ApiLinkType.EXCLUDED_URLS_HISTORY);
                }
            }
        }

    }

    @Required
    public void setApiV1BaseUri(URI apiV1BaseUri) {
        this.apiV1BaseUri = apiV1BaseUri;
    }

    @Required
    public void setApiV2BaseUri(URI apiV2BaseUri) {
        this.apiV2BaseUri = apiV2BaseUri;
    }

    @Required
    public void setApiV3BaseUri(URI apiV3BaseUri) {
        this.apiV3BaseUri = apiV3BaseUri;
    }

    private static URI appendPath(URI uri, StringBuilder pathToAppend) {
        if (pathToAppend == null) {
            throw new NullPointerException("Path is null");
        }
        if (pathToAppend.charAt(0) != '/') {
            throw new IllegalArgumentException("Path must with /: " + pathToAppend);
        }
        String originalPath = uri.getPath();
        StringBuilder resultPath;
        if (originalPath == null) {
            resultPath = pathToAppend;
        } else {
            resultPath = new StringBuilder(originalPath.length() + pathToAppend.length());
            if (originalPath.charAt(originalPath.length() - 1) == '/') {
                resultPath.append(originalPath.substring(0, originalPath.length() - 1));
            } else {
                resultPath.append(originalPath);
            }

            resultPath.append(pathToAppend);
        }

        try {
            return new URI(
                    uri.getScheme(),
                    uri.getUserInfo(),
                    uri.getHost(),
                    uri.getPort(),
                    resultPath.toString(),
                    uri.getQuery(),
                    uri.getFragment());
        } catch (URISyntaxException e) {
            throw new RuntimeException("Unable to create uri", e);
        }
    }
}
