package com.centzy.cannon.normalizer.impl;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;

import com.centzy.cannon.CanonicalUrl;
import com.centzy.cannon.Url;
import com.google.common.base.Throwables;
import com.google.inject.Inject;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.function.Function;

/**
 * @author Steven Fraim (steven@locality.com)
 */
class UrlNormalizerImpl implements Function<Url, CanonicalUrl> {

  private static final String DEFAULT_SCHEME = "http";

  @Inject
  UrlNormalizerImpl() { }

  @Override
  public CanonicalUrl apply(Url request) {
    return toCanonicalUrl(validate(heuristicReplace(clean(request.getValue()))));
  }

  private static String clean(String freeformUrl) {
    return freeformUrl.trim().replaceAll(" ", "");
  }

  private static URI heuristicReplace(String freeformUrl) {
    try {
      URI uri = new URI(freeformUrl);

      if (uri.getScheme() == null) {
        uri = getUriWithDefaultScheme(uri);
      }
      return uri;
    } catch (URISyntaxException e) {
      throw Throwables.propagate(e);
    }
  }

  private static URI validate(URI uri) {
    checkNotNull(uri.getScheme(), "Unable to parse scheme from " + uri.toString());
    checkNotNull(uri.getHost(), "Unable to parse host from " + uri.toString());
    return uri;
  }

  private static CanonicalUrl toCanonicalUrl(URI uri) {
    CanonicalUrl.Builder canonicalUrlBuilder = CanonicalUrl.newBuilder()
        .setUriScheme(CanonicalUrl.UriScheme.valueOf(uri.getScheme().toUpperCase()))
        .setHost(uri.getHost());

    if (uri.getPort() != -1) {
      canonicalUrlBuilder.setPort(uri.getPort());
    }

    if (!isNullOrEmpty(uri.getPath())) {
      canonicalUrlBuilder.setPath(uri.getPath());
    }

    if (!isNullOrEmpty(uri.getQuery())) {
      canonicalUrlBuilder.setQueryString(uri.getQuery());
    }
    if (!isNullOrEmpty(uri.getFragment())) {
      canonicalUrlBuilder.setFragmentIdentifier(uri.getFragment());
    }

    return canonicalUrlBuilder.build();
  }

  private static URI getUriWithDefaultScheme(URI uri) {
    try {
      return new URI(DEFAULT_SCHEME + "://" + uri.toString());
    } catch (URISyntaxException e) {
      throw Throwables.propagate(e);
    }
  }
}
