package com.atlassian.bitbucket;

import com.google.common.collect.Iterables;

import java.util.*;

/**
 * Collection of utility methods to generate various test data for testing.
 *
 */
public final class DataGenerationUtils {

    /** Max length of user name is 255 characters and all symbols are allowed */
    public static final String MAX_STASH_USER_NAME = "user_name with MiXeDcase and.lots.of.dots spaces àáâãäåçèéêëìíîðñòôõöö ~`!@#$%^&*()_+-={}|[]\\:\";<>?,./'国際交流λληνιабвгдежзبْجَدِيَّة عَرَبِيَّة‎עִבְרִיתदिंदुसरोवर1234567890_long_long_long long    _<script>alert(123)</script>////..    255 characters in total";

    /** Max length of user display name is 255 characters and all symbols are allowed */
    public static final String MAX_STASH_USER_DISPLAY_NAME = "User Display Name with MiXeDcase and.lots.of.dots spaces àáâãäåçèéêëìíîðñòôõöö ~`!@#$%^&*()_+-={}|[]\\:\";<>?,./'国際交流λληνιабвгдежзبْجَدِيَّة عَرَبِيَّة‎עִבְרִיתदिंदुसरोवर1234567890_long_long    _<script>alert(123)</script>////..      255 characters in total";

    /** Looks like there's no consensus on that topic yet, one limit is 64 for user part and that's what this method will return. In future it's going to be extended to 256
     Also there few more symbols allowed but they are not widely used and not supported by all clients/servers */
    public static final String MAX_EMAIL_ADDRESS = "abcdefghijklmnopqrstuvwxyz.ABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789@ver_long_domain.name.com.au";


    /** Max length of project name is 64 characters and all symbols are allowed */
    public static final String MAX_PROJECT_NAME = "Project à ~`!@#$%^&*()_+-={}|[]\\:\";<>?,./'国ληιжзِيَّةदिंदुसरोव12";

    /** Max length of project key is 64 characters and it's limited to a-zA-Z0-9 and underscores and should start with letter */
    public static final String MAX_PROJECT_KEY = "Long_lonG_1_Long_lonG_2_Long_lonG_3_Long_lonG_4_Long_lonG_6_Lon";

    /** Max length of project description is 255 characters and all symbols allowed */
    public static final String MAX_PROJECT_DESCRIPTION = "Super Long and Complicated Project DESCRIPTION with ~`!@#$%^&*()_+-={}|[]\\:\";<>?,./' àáâãäåçèéêëìíîðñòôõöö 国際交流абвгдежзبْجَدِيَّة عَرَبِيَّة‎עִבְרִיתदिंदुसरोवरλληνι characters                           and a litle bit a the end_with_dashesh.and.dots = 255";

    /** Max length of group name is 255 characters and all characters allowed */
    public static final String MAX_GROUP_NAME = "Super Long and Complicated Group Name with ~`!@#$%^&*()_+-={}|[]\\:\";<>?,./' àáâãäåçèéêëìíîðñòôõöö 国際交流абвгдежзبْجَدِيَّة عَرَبِيَّة‎עִבְרִיתदिंदुसरोवरλληνι characters                           and a litle bit a the end_with_dashesh.and.dot     s     = 255";

    /** Max length of repo name is 64 with space, hyphen, underscore, dot and a-Z0-9 allowed */
    public static final String MAX_REPO_NAME = "repo     -_.long.Long-LONG _LONG_01234567890_LoNg_repo_name NaMe";

    /** Max length of repo slug is 64 with space(s) transformed to a hyphen, hyphen, underscore, dot and a-Z0-9 allowed */
    public static final String MAX_REPO_SLUG = "repo--_.long.long-long-_long_01234567890_long_repo_name-name";

    /** Max length of pull request title is 255 characters and all symbols allowed */
    public static final String MAX_PR_TITLE = "Super Long and Complicated Pull Request Title with ~`!@#$%^&*()_+-={}|[]\\:\";<>?,./' àáâãäåçèéêëìíîðñòôõöö 国際交流абвгдежзبْجَدِيَّة عَرَبِيَّة‎עִבְרִיתदिंदुसरोवरλληνι characters                           and a litle bit a the end_with_dashesh.and.dots =  255";

    /** Max length of pull request description is 32K, for majority of tests 255 symbols will be enough, 32k is tested separately */
    public static final String MAX_PR_DESCRIPTION = "Super Long and Complicated Pull Request Description with ~`!@#$%^&*()_+-={}|[]\\:\";<>?,./' àáâãäåçèéêëìíîðñòôõöö 国際交流абвгдежзبْجَدِيَّة عَرَبِيَّة‎עִבְרִיתदिंदुसरोवरλληνι characters                           and a litle bit a the end_with_dashesh.and.dots =  255";


    /**
     * Enum that holds all types of characters split into different buckets.
     * random() method can be called for each bucket to return one random character from that bucket.
     */
    public enum Characters {

        CONTROL_CHARACTERS('\b', '\n', '\r', '\t', '\f'),
        PROBLEMATIC_CHARACTERS('!', '"', '\'', '\\', '/', '#', '$', '%', '&', '(', ')', '*', '+', '-', '_', '|', '[', ']', '<', '>', ':', ';', '`', '@', '^', '{', '}', '~'),
        ONE_BYTE_UNICODE_CHARACTER(0x0020, 0x007F),
        TWO_BYTE_UNICODE_CHARACTER(0x00a0, 0x07C0),
        THREE_BYTE_UNICODE_CHARACTER(0x0800, 0x0F000),
        FOUR_BYTE_UNICODE_CHARACTER(0x10000, 0x10FFF);

        private final List<Character> characters;
        private final Random rand = new Random();

        private Characters(Character... characters) {
            this.characters = new ArrayList<Character>(Arrays.asList(characters));
        }

        private Characters(int from, int to) {
            this(generateUnicodeCharacters(from, to));
        }

        // Return random character from a bucket
        public char random() {
            return characters.get(rand.nextInt(characters.size()));
        }

        /**
         * Generates unicode characters from specified byte range
         * @param from start of the character range
         * @param to end of the character range
         * @return character array that represents the range
         */
        private static Character[] generateUnicodeCharacters(int from, int to) {
            List<Character> characters = new ArrayList<Character>(to - from);

            for (int i = from; i < to; i++) {
                characters.add((char) i);
            }
            return characters.toArray(new Character[characters.size()]);
        }
    }

    /**
     * Generates random string of specified length that will contain characters of all different types @see Characters enum for more details
     * @param length length of the string to be generated
     * @return random string of specified length that will contain characters of various types
     */
    public static String generateString(int length) {
        StringBuilder sb = new StringBuilder(length);
        // Cycle through available buckets and pick one character from each
        Iterator<Characters> buckets =  Iterables.cycle(Characters.values()).iterator();
        while (sb.length() < length) {
            sb.append(buckets.next().random());
        }
        return sb.toString();
    }


}