package com.atlassian.bitbucket.internal.branch.web;

import org.apache.commons.lang3.StringUtils;

import java.text.Normalizer;
import java.util.Arrays;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Stream;

import static com.atlassian.bitbucket.util.MoreStreams.streamOptional;
import static java.util.Optional.empty;
import static java.util.Optional.of;

public class BranchNameFormatter {

    private BranchNameFormatter() {
        // utility class
    }

    /**
     * Creates a branch name from an issue key and issue summary. Starts truncation at a certain length
     * but does not cut off a word. Everything including '-' and '_' except spaces are preserved as a single word.
     * Issue summary is slugified in parts.
     *
     * e.g. if truncationStartLength = 20, issueKey = 'STASH-12345' and issueSummary = 'hello world and universe'
     * this would use up to 20 characters from the issue key and issue summary, then complete the word
     * returning "STASH-12345-hello-world".
     *
     * @param issueKey issue key
     * @param issueSummary issue summary
     * @param truncationStartLength after the generated name reaches this length, don't add any more parts to it
     * @return optional branch name generated from issue key and issue summary
     */
    public static Optional<String> generateBranchNameFromIssue(Optional<String> issueKey, Optional<String> issueSummary,
                                                               int truncationStartLength) {
        if (issueKey.isPresent() || issueSummary.isPresent()) {
            Stream<String> keyPart = streamOptional(issueKey);
            Stream<String> summaryParts = streamOptional(issueSummary)
                    .flatMap(summary -> Arrays.stream(summary.split("\\s+")))
                    .map(BranchNameFormatter::slugify)
                    .filter(summary -> !StringUtils.isBlank(summary));
            return of(joinWithMax(Stream.concat(keyPart, summaryParts), "-", truncationStartLength));
        }
        return empty();
    }

    private static String joinWithMax(Stream<String> parts, String separator, int truncationStartLength) {
        StringBuilder sb = new StringBuilder();
        parts.forEach(part -> {
            if (sb.length() < truncationStartLength) {
                if (sb.length() != 0) {
                    sb.append(separator);
                }
                sb.append(part);
            }
        });
        return sb.toString();
    }

    private static String slugify(String name) {
        return StringUtils.strip(Normalizer.normalize(name, Normalizer.Form.NFKD)
                .replaceAll("[^\\p{InBasicLatin}]+", "")
                .replaceAll("[^a-zA-Z\\-_0-9\\.]+", "-")
                .replaceAll("\\.\\.", "")
                .replaceAll("\\.$", "")
                .toLowerCase(Locale.US), "-");
    }
}
