/*
 * Copyright 2016 Red Hat, Inc. and/or its affiliates
 * and other contributors as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.keycloak.subsystem.server.extension;

import java.util.ArrayList;
import java.util.List;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.dmr.ModelNode;
import org.junit.Test;
import static org.junit.Assert.*;

import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;

/**
 *
 * @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
 */
public class JsonConfigConverterTestCase {

    private final PathElement domainRoot = PathElement.pathElement("profile", "auth-server-clustered");
    private final PathAddress domainAddress = PathAddress.pathAddress(domainRoot)
                                                         .append(KeycloakExtension.PATH_SUBSYSTEM);
    private final PathAddress standaloneAddress = PathAddress.pathAddress(KeycloakExtension.PATH_SUBSYSTEM);

    @Test
    public void testConvertJsonStandaloneWithModules() throws Exception {
        String json = basicJsonConfig(true);
        List<ModelNode> expResult = expectedOperations(true, false);
        
        List<ModelNode> result = JsonConfigConverter.convertJsonConfig(json, standaloneAddress);
        assertEquals(expResult, result);
    }
    
    @Test
    public void testConvertJsonStandaloneWithoutModules() throws Exception {
        String json = basicJsonConfig(false);
        List<ModelNode> expResult = expectedOperations(false, false);
        
        List<ModelNode> result = JsonConfigConverter.convertJsonConfig(json, standaloneAddress);
        assertEquals(expResult, result);
    }
    
    @Test
    public void testConvertJsonDomainWithModules() throws Exception {
        String json = basicJsonConfig(true);
        List<ModelNode> expResult = expectedOperations(true, true);
        
        List<ModelNode> result = JsonConfigConverter.convertJsonConfig(json, domainAddress);
        assertEquals(expResult, result);
    }
    
    @Test
    public void testConvertJsonDomainWithoutModules() throws Exception {
        String json = basicJsonConfig(false);
        List<ModelNode> expResult = expectedOperations(false, true);
        
        List<ModelNode> result = JsonConfigConverter.convertJsonConfig(json, domainAddress);
        assertEquals(expResult, result);
    }
    
    private String basicJsonConfig(boolean includeModules) {
        String basicConfig = 
              "{\n"
            + "    \"providers\": [\n"
            + "        \"classpath:${jboss.home.dir}/providers/*\"\n"
            + "    ],\n"
            + "\n"
            + "    \"admin\": {\n"
            + "        \"realm\": \"master\"\n"
            + "    },\n"
            + "\n"
            + "    \"eventsStore\": {\n"
            + "        \"provider\": \"jpa\",\n"
            + "        \"jpa\": {\n"
            + "            \"exclude-events\": [ \"REFRESH_TOKEN\" ]\n"
            + "        }\n"
            + "    },\n"
            + "\n"
            + "    \"realm\": {\n"
            + "        \"provider\": \"jpa\"\n"
            + "    },\n"
            + "\n"
            + "    \"user\": {\n"
            + "        \"provider\": \"jpa\"\n"
            + "    },\n"
            + "\n"
            + "    \"userCache\": {\n"
            + "        \"default\" : {\n"
            + "            \"enabled\": true\n"
            + "        }\n"
            + "    },\n"
            + "\n"
            + "    \"userSessionPersister\": {\n"
            + "        \"provider\": \"jpa\"\n"
            + "    },\n"
            + "\n"
            + "    \"authorizationPersister\": {\n"
            + "        \"provider\": \"jpa\"\n"
            + "    },\n"
            + "\n"
            + "    \"timer\": {\n"
            + "        \"provider\": \"basic\"\n"
            + "    },\n"
            + "\n"
            + "    \"theme\": {\n"
            + "        \"staticMaxAge\": 2592001,\n"
            + "        \"cacheTemplates\": false,\n"
            + "        \"cacheThemes\": false,\n"
            + "        \"welcomeTheme\": \"welcome\",\n"
            + "        \"default\": \"default\",\n"
            + "        \"folder\": {\n"
            + "          \"dir\": \"${jboss.home.dir}/themes\"\n";
            
        
        if (includeModules) {
            basicConfig +=
              "        },\n"
            + "        \"module\": {\n"
            + "          \"modules\": [ \"org.keycloak.example.themes\" ]\n"
            + "         }\n";
        } else {
            basicConfig +=
              "        }\n";
        }
        
        basicConfig +=
              "     },\n"
            + "\n"
            + "    \"scheduled\": {\n"
            + "        \"interval\": 900\n"
            + "    },\n"
            + "\n"
            + "    \"connectionsHttpClient\": {\n"
            + "        \"default\": {}\n"
            + "    },\n"
            + "\n"
            + "    \"connectionsJpa\": {\n"
            + "        \"default\": {\n"
            + "            \"dataSource\": \"java:jboss/datasources/KeycloakDS\",\n"
            + "            \"databaseSchema\": \"update\"\n"
            + "        }\n"
            + "    },\n"
            + "\n"
            + "    \"realmCache\": {\n"
            + "        \"default\" : {\n"
            + "            \"enabled\": true\n"
            + "        }\n"
            + "    },\n"
            + "\n"
            + "    \"connectionsInfinispan\": {\n"
            + "        \"provider\": \"default\",\n"
            + "        \"default\": {\n"
            + "            \"cacheContainer\" : \"java:comp/env/infinispan/Keycloak\"\n"
            + "        }\n"
            + "    }\n"
            + "}";
        
        return basicConfig;
    }
    
    private List<ModelNode> expectedOperations(boolean includeModules, boolean isDomain) {
        List<ModelNode> ops = new ArrayList<>();
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"write-attribute\",\n" +
            "    \"address\" => [(\"subsystem\" => \"keycloak-server\")],\n" +
            "    \"name\" => \"master-realm-name\",\n" +
            "    \"value\" => \"master\"\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"write-attribute\",\n" +
            "    \"address\" => [(\"subsystem\" => \"keycloak-server\")],\n" +
            "    \"name\" => \"scheduled-task-interval\",\n" +
            "    \"value\" => 900L\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"write-attribute\",\n" +
            "    \"address\" => [(\"subsystem\" => \"keycloak-server\")],\n" +
            "    \"name\" => \"providers\",\n" +
            "    \"value\" => [\"classpath:${jboss.home.dir}/providers/*\"]\n" +
            "}"
        ));
        
        if (includeModules) {
            ops.add(ModelNode.fromString(
                "{\n" +
                "    \"operation\" => \"add\",\n" +
                "    \"address\" => [\n" +
                "        (\"subsystem\" => \"keycloak-server\"),\n" +
                "        (\"theme\" => \"defaults\")\n" +
                "    ],\n" +
                "    \"staticMaxAge\" => 2592001L,\n" +
                "    \"cacheTemplates\" => false,\n" +
                "    \"cacheThemes\" => false,\n" +
                "    \"dir\" => \"${jboss.home.dir}/themes\",\n" +
                "    \"welcomeTheme\" => \"welcome\",\n" +
                "    \"default\" => \"default\",\n" +
                "    \"modules\" => [\"org.keycloak.example.themes\"]\n" +
                "}"
            ));
        } else {
            ops.add(ModelNode.fromString(
                "{\n" +
                "    \"operation\" => \"add\",\n" +
                "    \"address\" => [\n" +
                "        (\"subsystem\" => \"keycloak-server\"),\n" +
                "        (\"theme\" => \"defaults\")\n" +
                "    ],\n" +
                "    \"staticMaxAge\" => 2592001L,\n" +
                "    \"cacheTemplates\" => false,\n" +
                "    \"cacheThemes\" => false,\n" +
                "    \"dir\" => \"${jboss.home.dir}/themes\",\n" +
                "    \"welcomeTheme\" => \"welcome\",\n" +
                "    \"default\" => \"default\",\n" +
                "}"
            ));
        }
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"eventsStore\")\n" +
            "    ],\n" +
            "    \"default-provider\" => \"jpa\"\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"eventsStore\"),\n" +
            "        (\"provider\" => \"jpa\")\n" +
            "    ],\n" +
            "    \"properties\" => {\"exclude-events\" => \"[\\\"REFRESH_TOKEN\\\"]\"},\n" +
            "    \"enabled\" => true\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"realm\")\n" +
            "    ],\n" +
            "    \"default-provider\" => \"jpa\"\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"user\")\n" +
            "    ],\n" +
            "    \"default-provider\" => \"jpa\"\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"userCache\")\n" +
            "    ]\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"userCache\"),\n" +
            "        (\"provider\" => \"default\")\n" +
            "    ],\n" +
            "    \"enabled\" => true\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"userSessionPersister\")\n" +
            "    ],\n" +
            "    \"default-provider\" => \"jpa\"\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"authorizationPersister\")\n" +
            "    ],\n" +
            "    \"default-provider\" => \"jpa\"\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"timer\")\n" +
            "    ],\n" +
            "    \"default-provider\" => \"basic\"\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"connectionsHttpClient\")\n" +
            "    ]\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"connectionsHttpClient\"),\n" +
            "        (\"provider\" => \"default\")\n" +
            "    ],\n" +
            "    \"enabled\" => true\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"connectionsJpa\")\n" +
            "    ]\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"connectionsJpa\"),\n" +
            "        (\"provider\" => \"default\")\n" +
            "    ],\n" +
            "    \"properties\" => {\n" +
            "        \"dataSource\" => \"java:jboss/datasources/KeycloakDS\",\n" +
            "        \"databaseSchema\" => \"update\"\n" +
            "    },\n" +
            "    \"enabled\" => true\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"realmCache\")\n" +
            "    ]\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"realmCache\"),\n" +
            "        (\"provider\" => \"default\")\n" +
            "    ],\n" +
            "    \"enabled\" => true\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"connectionsInfinispan\")\n" +
            "    ],\n" +
            "    \"default-provider\" => \"default\"\n" +
            "}"
        ));
        
        ops.add(ModelNode.fromString(
            "{\n" +
            "    \"operation\" => \"add\",\n" +
            "    \"address\" => [\n" +
            "        (\"subsystem\" => \"keycloak-server\"),\n" +
            "        (\"spi\" => \"connectionsInfinispan\"),\n" +
            "        (\"provider\" => \"default\")\n" +
            "    ],\n" +
            "    \"properties\" => {\"cacheContainer\" => \"java:comp/env/infinispan/Keycloak\"},\n" +
            "    \"enabled\" => true\n" +
            "}"
        ));

        if (isDomain) { // prepend the domain root
            for (ModelNode op : ops) {
                PathAddress addr = PathAddress.pathAddress(op.get(ADDRESS));
                PathAddress domainAddr = PathAddress.pathAddress(domainRoot).append(addr);
                op.get(ADDRESS).set(domainAddr.toModelNode());
            }
        }
        
        return ops;
    }
}