package com.atlassian.stash.internal.liquibase;

import com.atlassian.bitbucket.annotation.Dao;
import com.atlassian.bitbucket.annotation.DataSet;
import com.atlassian.stash.internal.AbstractDaoTest;
import com.atlassian.stash.internal.DataSets;
import com.atlassian.stash.internal.user.HibernateStashUserDao;
import com.atlassian.stash.internal.user.InternalApplicationUser;
import com.atlassian.stash.internal.user.InternalNormalUser;
import com.atlassian.stash.internal.user.StashUserDao;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import org.apache.commons.lang3.StringUtils;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import javax.sql.DataSource;

/**
 * Tests that {@link SetStashUserSlugChange} correctly updates the {@code stash_user} table with user slugs.
 * <p>
 * {@code set_stash_user_slugs.xml} contains stash_user entries which is inserted by DbUnit. That data creates
 * a number of {@link InternalApplicationUser users} which exercise various paths
 * in the change.
 * <p>
 * After the change is run, Hibernate is used to retrieve the users. If the slugs have not been updated correctly
 * the tests will fail.
 */
@DataSet(DataSets.PREFIX + "liquibase/set_stash_user_slugs" + DataSets.SUFFIX)
@SuppressWarnings("ConstantConditions")
@Ignore("This test can't be run at present as it expects to be run after a certain set of Liquibase changesets " +
        "have been executed but is actually run after all have been executed and the schema, especially the " +
        "user tables, has advanced too far.")
public class SetApplicationUserSlugChangeTest extends AbstractDaoTest {

    @Autowired
    private DataSource dataSource;
    @Dao(HibernateStashUserDao.class)
    private StashUserDao userDao;

    @Test
    public void testExecute() throws Exception {
        JdbcConnection connection = new JdbcConnection(dataSource.getConnection());
        Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection);

        SetStashUserSlugChange change = new SetStashUserSlugChange();
        change.execute(database);
        assertEquals("All stash_user slug values were set", change.getConfirmationMessage());

        //Verifies permutations of illegal characters in usernames are all escaped as espected
        assertEquals(1, userDao.findBySlug("user").getId());
        assertEquals(2, userDao.findBySlug("foo__bar").getId());
        assertEquals(15, userDao.findBySlug("user with spaces in name").getId());
        assertEquals(16, userDao.findBySlug("user_20with_spaces").getId());
        assertEquals(17, userDao.findBySlug("symbols_____^_____").getId());
        assertEquals(18, userDao.findBySlug("_separators___{}").getId());
        assertEquals(19, userDao.findBySlug("that_s whack_ yo").getId());
        assertEquals(20, userDao.findBySlug("-b - sqrt_b^2 - 4ac__2a").getId());
        assertEquals(21, userDao.findBySlug("2_2_5_").getId());
        assertEquals(22, userDao.findBySlug("john.doe").getId());
        assertEquals(23, userDao.findBySlug("鲁迅").getId());

        //Verify users 3 through 14, where 3 should have gotten a token because 4-14 used its possible slugs
        String base = "foo_bar_baz";
        assertEquals(4, userDao.findBySlug(base).getId());
        for (int i = 0; i < InternalNormalUser.MAX_SLUG_RETRY_COUNT; ++i) {
            assertEquals(i + 5, userDao.findBySlug(base + i).getId());
        }
        assertFalse("Slug calculation should have given up and used a secure token",
                userDao.getById(3).getSlug().startsWith(base));

        base = StringUtils.repeat("_", InternalApplicationUser.MAX_GENERATED_SLUG_LENGTH);
        assertEquals(24, userDao.findBySlug(base).getId()); //24 should have gotten the non-suffixed base
        for (int i = 0; i < InternalNormalUser.MAX_SLUG_RETRY_COUNT; ++i) {
            //25-34 should have gotten the 0-9 suffixes
            assertEquals(i + 25, userDao.findBySlug(base + i).getId());
        }
        //35 should have gotten a token
        String slug = userDao.getById(35).getSlug();
        assertFalse("Slug calculation should have used a secure token and not be related to the name", slug.startsWith("/"));
        assertTrue("Slug calculation should have given up and used a secure token", slug.matches("[a-f0-9]+"));
    }
}
