package org.keycloak.model.test;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Set;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.Config;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.util.JsonSerialization;

/**
 * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
 */
public class AbstractModelTest {

    protected static KeycloakSessionFactory factory;
    protected static ProviderSessionFactory providerSessionFactory;

    protected KeycloakSession identitySession;
    protected RealmManager realmManager;
    protected ProviderSession providerSession;

    @BeforeClass
    public static void beforeClass() {
        factory = KeycloakApplication.createSessionFactory();
        providerSessionFactory = KeycloakApplication.createProviderSessionFactory();

        KeycloakSession identitySession = factory.createSession();
        try {
            identitySession.getTransaction().begin();
            new ApplianceBootstrap().bootstrap(identitySession, "/auth");
            identitySession.getTransaction().commit();
        } finally {
            identitySession.close();
        }
    }

    @AfterClass
    public static void afterClass() {
        providerSessionFactory.close();
        factory.close();
    }

    @Before
    public void before() throws Exception {
        identitySession = factory.createSession();
        identitySession.getTransaction().begin();
        realmManager = new RealmManager(identitySession);

        providerSession = providerSessionFactory.createSession();
    }

    @After
    public void after() throws Exception {
        identitySession.getTransaction().commit();
        providerSession.close();
        identitySession.close();

        identitySession = factory.createSession();
        try {
            identitySession.getTransaction().begin();

            RealmManager rm = new RealmManager(identitySession);
            for (RealmModel realm : identitySession.getRealms()) {
                if (!realm.getName().equals(Config.getAdminRealm())) {
                    rm.removeRealm(realm);
                }
            }

            identitySession.getTransaction().commit();
        } finally {
            identitySession.close();
        }

    }

    protected void commit() {
        commit(false);
    }

    protected void commit(boolean rollback) {
        if (rollback) {
            identitySession.getTransaction().rollback();
        } else {
            identitySession.getTransaction().commit();
        }
        resetSession();
    }

    protected void resetSession() {
        identitySession.close();
        identitySession = factory.createSession();
        identitySession.getTransaction().begin();
        realmManager = new RealmManager(identitySession);
    }

    public static RealmRepresentation loadJson(String path) throws IOException {
        InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        int c;
        while ((c = is.read()) != -1) {
            os.write(c);
        }
        byte[] bytes = os.toByteArray();
        return JsonSerialization.readValue(bytes, RealmRepresentation.class);
    }


    // Helper methods for role equality

    public static void assertRolesEquals(Set<RoleModel> expected, Set<RoleModel> actual) {
        Assert.assertEquals(expected.size(), actual.size());
        for (RoleModel current : actual) {
            assertRolesContains(current, expected);
        }
    }

    public static void assertRolesContains(RoleModel expected, Set<RoleModel> actual) {
        for (RoleModel current : actual) {
            if (current.getId().equals(expected.getId())) {
                assertRolesEquals(current, expected);
                return;
            }
        }

        Assert.fail("Role with id=" + expected.getId() + " name=" + expected.getName() + " not in set " + actual);
    }

    public static void assertRolesEquals(RoleModel expected, RoleModel actual) {
        Assert.assertEquals(expected.getId(), actual.getId());
        Assert.assertEquals(expected.getName(), actual.getName());
        Assert.assertEquals(expected.getDescription(), actual.getDescription());
        Assert.assertEquals(expected.getContainer(), actual.getContainer());
        Assert.assertEquals(expected.getComposites().size(), actual.getComposites().size());
    }
}
